/* Ordered Dictionary object implementation using a hash table and a vector of
pointers to the items.
*/
/*
This file has been directly derived from and retains many algorithms from
objectdict.c in the Python 2.5.1 source distribution. Its licensing therefore
is governed by the license as distributed with Python 2.5.1 available in the
file LICNESE in the source distribution of ordereddict
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software
Foundation; All Rights Reserved"
Copyrigh (c) 2007-10-13 onwards: Anthon van der Neut
*/
/*
Ordering by key insertion order (KIO) instead of key/val insertion order
(KVIO) is less expensive (as the list of keys does not have to be updated).
*/
#include "Python.h"
#include "ordereddict.h"
#if PY_VERSION_HEX < 0x03000000
#define PyUNISTR_Object PyStringObject
#define PyUNISTR_Concat PyString_Concat
#define PyUNISTR_ConcatAndDel PyString_ConcatAndDel
#define PyUNISTR_CheckExact PyString_CheckExact
#define PyUNISTR_FromString PyString_FromString
#define PyUNISTR_FromFormat PyString_FromFormat
#define PyUNISTR_Join _PyString_Join
#define PyUNISTR_Eq _PyString_Eq
#define Py_hash_t long
#define Py_hash_ssize_t Py_ssize_t
#define OB_HASH ob_shash
#else
/* Return 1 if two unicode objects are equal, 0 if not.
* unicode_eq() is called when the hash of two unicode objects is equal.
*/
#if PY_VERSION_HEX < 0x03030000
Py_LOCAL_INLINE(int)
unicode_eq(PyObject *aa, PyObject *bb)
{
register PyUnicodeObject *a = (PyUnicodeObject *)aa;
register PyUnicodeObject *b = (PyUnicodeObject *)bb;
if (a->length != b->length)
return 0;
if (a->length == 0)
return 1;
if (a->str[0] != b->str[0])
return 0;
if (a->length == 1)
return 1;
return memcmp(a->str, b->str, a->length * sizeof(Py_UNICODE)) == 0;
}
#else
Py_LOCAL_INLINE(int)
unicode_eq(PyObject *aa, PyObject *bb)
{
register PyUnicodeObject *a = (PyUnicodeObject *)aa;
register PyUnicodeObject *b = (PyUnicodeObject *)bb;
if (PyUnicode_READY(a) == -1 || PyUnicode_READY(b) == -1) {
assert(0 && "unicode_eq ready fail");
return 0;
}
if (PyUnicode_GET_LENGTH(a) != PyUnicode_GET_LENGTH(b))
return 0;
if (PyUnicode_GET_LENGTH(a) == 0)
return 1;
if (PyUnicode_KIND(a) != PyUnicode_KIND(b))
return 0;
return memcmp(PyUnicode_1BYTE_DATA(a), PyUnicode_1BYTE_DATA(b),
PyUnicode_GET_LENGTH(a) * PyUnicode_KIND(a)) == 0;
}
#endif
#if PY_VERSION_HEX < 0x03030000
#define PyUNISTR_Object PyUnicodeObject
#else
#define PyUNISTR_Object PyASCIIObject
#endif
#define PyUNISTR_Concat PyUnicode_Append
#define PyUNISTR_ConcatAndDel PyUnicode_AppendAndDel
#define PyUNISTR_CheckExact PyUnicode_CheckExact
#define PyUNISTR_FromString PyUnicode_FromString
#define PyUNISTR_FromFormat PyUnicode_FromFormat
#define PyUNISTR_Join PyUnicode_Join
#define PyUNISTR_Eq unicode_eq
#define Py_hash_ssize_t Py_hash_t
#define OB_HASH hash
#endif
#if PY_VERSION_HEX < 0x02050000
#define SPR "%d"
#else
#define SPR "%ld"
#endif
#if PY_VERSION_HEX < 0x02080000
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
#endif
#ifdef NDEBUG
#undef NDEBUG
#endif
#define DEFERRED_ADDRESS(ADDR) 0
/* Set a key error with the specified argument, wrapping it in a
* tuple automatically so that tuple keys are not unpacked as the
* exception arguments. */
static void
set_key_error(PyObject *arg)
{
PyObject *tup;
tup = PyTuple_Pack(1, arg);
if (!tup)
return; /* caller will expect error to be set anyway */
PyErr_SetObject(PyExc_KeyError, tup);
Py_DECREF(tup);
}
/* Define this out if you don't want conversion statistics on exit. */
#undef SHOW_CONVERSION_COUNTS
/* See large comment block below. This must be >= 1. */
#define PERTURB_SHIFT 5
/*
see object/dictobject.c for subtilities of the base dict implementation
*/
/* Object used as dummy key to fill deleted entries */
static PyObject *dummy = NULL; /* Initialized by first call to newPyDictObject() */
#ifdef Py_REF_DEBUG
PyObject *
_PyOrderedDict_Dummy(void)
{
return dummy;
}
#endif
/* relaxed: allow init etc. of ordereddict from dicts if true */
static int ordereddict_relaxed = 0;
/* Key Value Insertion Order: rearrange at end on update if true */
static int ordereddict_kvio = 0;
/* forward declarations */
static PyOrderedDictEntry *
lookdict_string(PyOrderedDictObject *mp, PyObject *key, Py_hash_t hash);
int PyOrderedDict_CopySome(PyObject *a, PyObject *b,
Py_ssize_t start, Py_ssize_t step,
Py_ssize_t count, int override);
#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
/* Debug statistic to compare allocations with reuse through the free list */
#undef SHOW_ALLOC_COUNT
#ifdef SHOW_ALLOC_COUNT
static size_t count_alloc = 0;
static size_t count_reuse = 0;
static void
show_alloc(void)
{
fprintf(stderr, "Dict allocations: %" PY_FORMAT_SIZE_T "d\n",
count_alloc);
fprintf(stderr, "Dict reuse through freelist: %" PY_FORMAT_SIZE_T
"d\n", count_reuse);
fprintf(stderr, "%.2f%% reuse rate\n\n",
(100.0*count_reuse/(count_alloc+count_reuse)));
}
#endif
/* Initialization macros.
There are two ways to create a dict: PyOrderedDict_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 PyOrderedDict_New does because it's guaranteed
that the PyOrderedDictObject 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)->od_otablep = (mp)->ma_smallotablep; \
(mp)->ma_mask = PyOrderedDict_MINSIZE - 1; \
} while(0)
#define EMPTY_TO_MINSIZE(mp) do { \
memset((mp)->ma_smalltable, 0, sizeof((mp)->ma_smalltable)); \
memset((mp)->ma_smallotablep, 0, sizeof((mp)->ma_smallotablep)); \
(mp)->ma_used = (mp)->od_fill = (mp)->od_state = 0; \
INIT_NONZERO_DICT_SLOTS(mp); \
} while(0)
/* (mp)->od_cmp = (mp)->od_key = NULL; \*/
#define INIT_SORT_FUNCS(SD) do { \
SD->sd_cmp = Py_None; Py_INCREF(Py_None); \
SD->sd_key = Py_None; Py_INCREF(Py_None); \
SD->sd_value = Py_None; Py_INCREF(Py_None); \
} while(0)
#define OD_KVIO_BIT (1<<0)
#define OD_RELAXED_BIT (1<<1)
#define OD_REVERSE_BIT (1<<2)
#define KVIO(mp) (mp->od_state & OD_KVIO_BIT)
#define RELAXED(mp) (mp->od_state & OD_RELAXED_BIT)
#define REVERSE(mp) (mp->od_state & OD_REVERSE_BIT)
/* Dictionary reuse scheme to save calls to malloc, free, and memset */
#ifndef PyDict_MAXFREELIST
#define PyDict_MAXFREELIST 80
#endif
static PyOrderedDictObject *free_list[PyDict_MAXFREELIST];
static int numfree = 0;
void
PyOrderedDict_Fini(void)
{
PyOrderedDictObject *op;
while (numfree) {
op = free_list[--numfree];
assert(PyOrderedDict_CheckExact(op));
PyObject_GC_Del(op);
}
}
PyObject *
PyOrderedDict_New(void)
{
register PyOrderedDictObject *mp;
assert(dummy != NULL); /* initialisation in the module init */
#ifdef SHOW_CONVERSION_COUNTS
Py_AtExit(show_counts);
#endif
#ifdef SHOW_ALLOC_COUNT
Py_AtExit(show_alloc);
#endif
if (numfree) {
mp = free_list[--numfree];
assert (mp != NULL);
assert (Py_TYPE(mp) == &PyOrderedDict_Type);
_Py_NewReference((PyObject *)mp);
if (mp->od_fill) {
EMPTY_TO_MINSIZE(mp);
} else {
/* At least set ma_table and ma_mask; these are wrong
if an empty but presized dict is added to freelist */
INIT_NONZERO_DICT_SLOTS(mp);
}
assert (mp->ma_used == 0);
assert (mp->ma_table == mp->ma_smalltable);
assert (mp->od_otablep == mp->ma_smallotablep);
assert (mp->ma_mask == PyOrderedDict_MINSIZE - 1);
#ifdef SHOW_ALLOC_COUNT
count_reuse++;
#endif
} else {
mp = PyObject_GC_New(PyOrderedDictObject, &PyOrderedDict_Type);
if (mp == NULL)
return NULL;
EMPTY_TO_MINSIZE(mp);
#ifdef SHOW_ALLOC_COUNT
count_alloc++;
#endif
}
mp->ma_lookup = lookdict_string;
#ifdef SHOW_CONVERSION_COUNTS
++created;
#endif
PyObject_GC_Track(mp);
return (PyObject *)mp;
}
PyObject *
PySortedDict_New(void)
{
register PyOrderedDictObject *mp;
register PySortedDictObject *sd;
assert(dummy != NULL);
mp = (PyOrderedDictObject *) PyObject_GC_New(PySortedDictObject, &PySortedDict_Type);
if (mp == NULL)
return NULL;
EMPTY_TO_MINSIZE(mp);
mp->ma_lookup = lookdict_string;
sd = (PySortedDictObject*)mp;
INIT_SORT_FUNCS(sd);
#ifdef SHOW_CONVERSION_COUNTS
++created;
#endif
PyObject_GC_Track(mp);
return (PyObject *)mp;
}
/*
The basic lookup function used by all operations.
This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4.
Open addressing is preferred over chaining since the link overhead for
chaining would be substantial (100% with typical malloc overhead).
The initial probe index is computed as hash mod the table size. Subsequent
probe indices are computed as explained earlier.
All arithmetic on hash should ignore overflow.
(The details in this version are due to Tim Peters, building on many past
contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and
Christian Tismer).
lookdict() is general-purpose, and may return NULL if (and only if) a
comparison raises an exception (this was new in Python 2.5).
lookdict_string() below is specialized to string keys, comparison of which can
never raise an exception; that function can never return NULL. For both, when
the key isn't found a PyOrderedDictEntry* 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
PyOrderedDictEntry *.
*/
static PyOrderedDictEntry *
lookdict(PyOrderedDictObject *mp, PyObject *key, register Py_hash_t hash)
{
register size_t i;
register size_t perturb;
register PyOrderedDictEntry *freeslot;
register size_t mask = (size_t)mp->ma_mask;
PyOrderedDictEntry *ep0 = mp->ma_table;
register PyOrderedDictEntry *ep;
register int cmp;
PyObject *startkey;
i = (size_t)hash & mask;
ep = &ep0[i];
if (ep->me_key == NULL || ep->me_key == key)
return ep;
if (ep->me_key == dummy)
freeslot = ep;
else {
if (ep->me_hash == hash) {
startkey = ep->me_key;
Py_INCREF(startkey);
cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
Py_DECREF(startkey);
if (cmp < 0)
return NULL;
if (ep0 == mp->ma_table && ep->me_key == startkey) {
if (cmp > 0)
return ep;
} else {
/* The compare did major nasty stuff to the
* dict: start over.
* XXX A clever adversary could prevent this
* XXX from terminating.
*/
return lookdict(mp, key, hash);
}
}
freeslot = NULL;
}
/* In the loop, me_key == dummy is by far (factor of 100s) the
least likely outcome, so test for that last. */
for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {
i = (i << 2) + i + perturb + 1;
ep = &ep0[i & mask];
if (ep->me_key == NULL)
return freeslot == NULL ? ep : freeslot;
if (ep->me_key == key)
return ep;
if (ep->me_hash == hash && ep->me_key != dummy) {
startkey = ep->me_key;
Py_INCREF(startkey);
cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
Py_DECREF(startkey);
if (cmp < 0)
return NULL;
if (ep0 == mp->ma_table && ep->me_key == startkey) {
if (cmp > 0)
return ep;
} else {
/* The compare did major nasty stuff to the
* dict: start over.
* XXX A clever adversary could prevent this
* XXX from terminating.
*/
return lookdict(mp, key, hash);
}
} else if (ep->me_key == dummy && freeslot == NULL)
freeslot = ep;
}
assert(0); /* NOT REACHED */
return 0;
}
/*
* Hacked up version of lookdict which can assume keys are always strings;
* this assumption allows testing for errors during PyObject_RichCompareBool()
* to be dropped; string-string comparisons never raise exceptions. This also
* means we don't need to go through PyObject_RichCompareBool(); we can always
* use PyUNISTR_Eq() directly.
*
* This is valuable because dicts with only string keys are very common.
*/
static PyOrderedDictEntry *
lookdict_string(PyOrderedDictObject *mp, PyObject *key, register Py_hash_t hash)
{
register size_t i;
register size_t perturb;
register PyOrderedDictEntry *freeslot;
register size_t mask = (size_t)mp->ma_mask;
PyOrderedDictEntry *ep0 = mp->ma_table;
register PyOrderedDictEntry *ep;
/* 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 (!PyUNISTR_CheckExact(key)) {
#ifdef SHOW_CONVERSION_COUNTS
++converted;
#endif
mp->ma_lookup = lookdict;
return lookdict(mp, key, hash);
}
i = hash & mask;
ep = &ep0[i];
if (ep->me_key == NULL || ep->me_key == key)
return ep;
if (ep->me_key == dummy)
freeslot = ep;
else {
if (ep->me_hash == hash && PyUNISTR_Eq(ep->me_key, key))
return ep;
freeslot = NULL;
}
/* In the loop, me_key == dummy is by far (factor of 100s) the
least likely outcome, so test for that last. */
for (perturb = hash; ; perturb >>= PERTURB_SHIFT) {
i = (i << 2) + i + perturb + 1;
ep = &ep0[i & mask];
if (ep->me_key == NULL)
return freeslot == NULL ? ep : freeslot;
if (ep->me_key == key
|| (ep->me_hash == hash
&& ep->me_key != dummy
&& PyUNISTR_Eq(ep->me_key, key)))
return ep;
if (ep->me_key == dummy && freeslot == NULL)
freeslot = ep;
}
assert(0); /* NOT REACHED */
return 0;
}
static int
dump_ordereddict_head(register PyOrderedDictObject *mp)
{
if (mp == NULL) {
printf("ordereddict header printing received NULL");
return -1;
}
if (PySortedDict_CheckExact(mp))
printf("sorteddict");
else
printf("ordereddict");
printf(": fill " SPR ", ", mp->od_fill);
printf("used " SPR ", ", mp->ma_used);
printf("mask " SPR ", ", mp->ma_mask);
printf("mask " SPR ", ", mp->ma_mask);
printf("\nbits: ");
if (KVIO(mp))
printf("kvio ");
if (RELAXED(mp))
printf("relax ");
if (REVERSE(mp))
printf("reverse ");
printf("\n");
return 0;
}
static void
dump_sorteddict_fun(register PySortedDictObject *mp)
{
printf("cmp %p, key %p, value %p\n", mp->sd_cmp, mp->sd_key, mp->sd_value);
}
static void
dump_otablep(register PyOrderedDictObject *mp)
{
Py_ssize_t index;
PyOrderedDictEntry **p;
printf("mp %p\n", mp);
for (index = 0, p = mp->od_otablep; index < mp->ma_used; index++, p++) {
printf("index " SPR " %p %p\n", index, p, *p);
}
}
/*
https://github.com/pbrady/fastcache/issues/32
mentions no tracking with GC_TRACK in extensions
*/
/* #if (PY_VERSION_HEX < 0x02070000) */
#if 1
#define MAINTAIN_TRACKING(mp, key, value)
#define _PyDict_MaybeUntrack(x)
#else
#ifdef SHOW_TRACK_COUNT
#define INCREASE_TRACK_COUNT \
(count_tracked++, count_untracked--);
#define DECREASE_TRACK_COUNT \
(count_tracked--, count_untracked++);
#else
#define INCREASE_TRACK_COUNT
#define DECREASE_TRACK_COUNT
#endif
#define MAINTAIN_TRACKING(mp, key, value) \
do { \
if (!_PyObject_GC_IS_TRACKED(mp)) { \
if (_PyObject_GC_MAY_BE_TRACKED(key) || \
_PyObject_GC_MAY_BE_TRACKED(value)) { \
_PyObject_GC_TRACK(mp); \
INCREASE_TRACK_COUNT \
} \
} \
} while(0)
PyAPI_FUNC(void)
_PyOrderedDict_MaybeUntrack(PyObject *op)
{
PyDictObject *mp;
PyObject *value;
Py_ssize_t mask, i;
PyDictEntry *ep;
if (!PyDict_CheckExact(op) || !_PyObject_GC_IS_TRACKED(op))
return;
mp = (PyDictObject *) op;
ep = mp->ma_table;
mask = mp->ma_mask;
for (i = 0; i <= mask; i++) {
if ((value = ep[i].me_value) == NULL)
continue;
if (_PyObject_GC_MAY_BE_TRACKED(value) ||
_PyObject_GC_MAY_BE_TRACKED(ep[i].me_key))
return;
}
DECREASE_TRACK_COUNT
_PyObject_GC_UNTRACK(op);
}
#endif
/*
Internal routine to insert a new item into the table when you have entry object.
Used by insertdict.
*/
static int
insertdict_by_entry(register PyOrderedDictObject *mp, PyObject *key, Py_hash_t hash,
PyOrderedDictEntry *ep, PyObject *value, Py_ssize_t index)
{
PyObject *old_value;
Py_ssize_t oindex;
register PyOrderedDictEntry **epp = NULL;
MAINTAIN_TRACKING(mp, key, value);
if (ep->me_value != NULL) { /* updating a value */
old_value = ep->me_value;
ep->me_value = value;
if (index != -1) {
if (index == -2) /* kvio */
index = mp->ma_used-1;
for (oindex = 0, epp = mp->od_otablep; oindex < mp->ma_used;
oindex++, epp++)
if (*epp == ep)
break;
/* epp now points to item and oindex is its index (optimize?) */
/* if index == oindex we don't have to anything */
if (index < oindex) {
epp = mp->od_otablep;
epp += index;
memmove(epp + 1, epp, (oindex - index) * sizeof(PyOrderedDictEntry *));
*epp = ep;
} else if ((index == oindex + 1) && (index == mp->ma_used)) {
/* nothing to do for inserting beyond last with same key */
} else if (index > oindex) {
/*
printf("moving %d %d %p\n", index, oindex, epp);
dump_otablep(mp); */
memmove(epp, epp + 1, (index - oindex) * sizeof(PyOrderedDictEntry *));
mp->od_otablep[index] = ep;
/*
dump_otablep(mp);
*/
}
}
Py_DECREF(old_value); /* which **CAN** re-enter */
Py_DECREF(key);
} else { /* new value */
if (ep->me_key == NULL)
mp->od_fill++;
else {
assert(ep->me_key == dummy);
Py_DECREF(dummy);
}
ep->me_key = key;
ep->me_hash = (Py_ssize_t)hash;
ep->me_value = value;
if (index < 0)
mp->od_otablep[mp->ma_used] = ep;
else {
epp = mp->od_otablep;
epp += index;
/* make space */
memmove(epp + 1, epp, (mp->ma_used - index) * sizeof(PyOrderedDictEntry *));
*epp = ep;
}
mp->ma_used++;
}
return 0;
}
/*
Internal routine to insert a new item into the table.
Used both by the internal resize routine and by the public insert routine.
Eats a reference to key and one to value.
Returns -1 if an error occurred, or 0 on success.
*/
static int
insertdict(register PyOrderedDictObject *mp, PyObject *key, Py_hash_t hash,
PyObject *value, Py_ssize_t index)
{
register PyOrderedDictEntry *ep;
assert(mp->ma_lookup != NULL);
ep = mp->ma_lookup(mp, key, hash);
if (ep == NULL) {
Py_DECREF(key);
Py_DECREF(value);
return -1;
}
return insertdict_by_entry(mp, key, hash, ep, value, index);
}
/*
Internal routine to insert a new item into the table when you have entry object.
Used by insertdict.
*/
static int
insertsorteddict_by_entry(register PyOrderedDictObject *mp, PyObject *key, Py_hash_t hash,
PyOrderedDictEntry *ep, PyObject *value)
{
PyObject *old_value;
Py_ssize_t index = 0, lower, upper;
int res;
register PySortedDictObject *sd = (PySortedDictObject *) mp;
register PyOrderedDictEntry **epp = NULL;
MAINTAIN_TRACKING(mp, key, value);
if (ep->me_value != NULL) { /* updating a value */
old_value = ep->me_value;
ep->me_value = value;
Py_DECREF(old_value); /* which **CAN** re-enter */
Py_DECREF(key);
if (sd->sd_value != Py_None || sd->sd_cmp != Py_None) {
PyErr_SetString(PyExc_NotImplementedError,
"updating a value for a cmp/value sorted dict not implemented"
);
return -1;
}
} else { /* new value */
if (ep->me_key == NULL)
mp->od_fill++;
else {
assert(ep->me_key == dummy);
Py_DECREF(dummy);
}
ep->me_key = key;
ep->me_hash = (Py_ssize_t)hash;
ep->me_value = value;
/* determine epp */
epp = mp->od_otablep;
lower = 0;
upper = mp->ma_used;
if (sd->sd_key != Py_None && sd->sd_key != Py_True) {
PyObject *transkey;
PyObject *chkkey;
transkey = PyObject_CallFunctionObjArgs(sd->sd_key, key, NULL);
if (transkey == NULL)
transkey = key;
while (lower < upper) {
index = (lower+upper) / 2;
chkkey = PyObject_CallFunctionObjArgs(sd->sd_key,(epp[index])->me_key, NULL);
if (chkkey == NULL)
chkkey = (epp[index])->me_key;
res = PyObject_RichCompareBool(chkkey, transkey, Py_GT);
if (res == 0)
lower = index + 1;
else if (res == 1)
upper = index;
else
return -1; /* res was -1 -> error */
}
} else {
while (lower < upper) {
index = (lower+upper) / 2;
res = PyObject_RichCompareBool((epp[index])->me_key, key, Py_GT);
if (res == 0)
lower = index + 1;
else if (res == 1)
upper = index;
else
return -1; /* res was -1 -> error */
}
}
epp += lower;
/* make space */
memmove(epp + 1, epp, (mp->ma_used - lower) * sizeof(PyOrderedDictEntry *));
*epp = ep;
mp->ma_used++;
}
return 0;
}
static int
insertsorteddict(register PyOrderedDictObject *mp, PyObject *key, Py_hash_t hash,
PyObject *value)
{
register PyOrderedDictEntry *ep;
/* printf("insert sorted dict\n"); */
assert(mp->ma_lookup != NULL);
ep = mp->ma_lookup(mp, key, hash);
if (ep == NULL) {
Py_DECREF(key);
Py_DECREF(value);
return -1;
}
return insertsorteddict_by_entry(mp, key, hash, ep, value);
}
/*
Internal routine used by dictresize() to insert an item which is
known to be absent from the dict. This routine also assumes that
the dict contains no deleted entries. Besides the performance benefit,
using insertdict() in dictresize() is dangerous (SF bug #1456209).
Note that no refcounts are changed by this routine; if needed, the caller
is responsible for incref'ing `key` and `value`.
*/
static void
insertdict_clean(register PyOrderedDictObject *mp, PyObject *key, Py_hash_t hash,
PyObject *value)
{
register size_t i;
register size_t perturb;
register size_t mask = (size_t)mp->ma_mask;
PyOrderedDictEntry *ep0 = mp->ma_table;
register PyOrderedDictEntry *ep;
MAINTAIN_TRACKING(mp, key, value);
i = hash & mask;
ep = &ep0[i];
for (perturb = hash; ep->me_key != NULL; perturb >>= PERTURB_SHIFT) {
i = (i << 2) + i + perturb + 1;
ep = &ep0[i & mask];
}
assert(ep->me_value == NULL);
mp->od_fill++;
ep->me_key = key;
ep->me_hash = (Py_ssize_t)hash;
ep->me_value = value;
mp->od_otablep[mp->ma_used] = ep;
mp->ma_used++;
}
/*
Restructure the table by allocating a new table and reinserting all
items again. When entries have been deleted, the new table may
actually be smaller than the old one.
*/
static int
dictresize(PyOrderedDictObject *mp, Py_ssize_t minused)
{
Py_ssize_t newsize;
PyOrderedDictEntry *oldtable, *newtable, *ep, **epp;
PyOrderedDictEntry **oldotablep, **newotablep;
register Py_ssize_t i, j;
int is_oldtable_malloced;
int reusing_smalltable;
PyOrderedDictEntry small_copy[PyOrderedDict_MINSIZE];
PyOrderedDictEntry *small_ocopyp[PyOrderedDict_MINSIZE];
assert(minused >= 0);
/* Find the smallest table size > minused. */
for (newsize = PyOrderedDict_MINSIZE;
newsize <= minused && newsize > 0;
newsize <<= 1)
;
if (newsize <= 0) {
PyErr_NoMemory();
return -1;
}
/* Get space for a new table. */
oldtable = mp->ma_table;
oldotablep = mp->od_otablep;
assert(oldtable != NULL);
assert(oldotablep != NULL);
is_oldtable_malloced = oldtable != mp->ma_smalltable;
reusing_smalltable = 0;
if (newsize == PyOrderedDict_MINSIZE) {
/* A large table is shrinking, or we can't get any smaller. */
newtable = mp->ma_smalltable;
newotablep = mp->ma_smallotablep;
if (newtable == oldtable) {
if (mp->od_fill == mp->ma_used) {
/* No dummies, so no point doing anything. */
return 0;
}
/* We're not going to resize it, but rebuild the
table anyway to purge old dummy entries.
Subtle: This is *necessary* if fill==size,
as lookdict needs at least one virgin slot to
terminate failing searches. If fill < size, it's
merely desirable, as dummies slow searches. */
assert(mp->od_fill > mp->ma_used);
memcpy(small_copy, oldtable, sizeof(small_copy));
/* Small_ocopyp must point into small_copy */
for (i = 0; i < PyOrderedDict_MINSIZE; i++) {
small_ocopyp[i] = oldotablep[i] ? &small_copy[oldotablep[i]-&oldtable[0]]: NULL;
}
oldtable = small_copy;
reusing_smalltable = 1;
}
} else {
newtable = PyMem_NEW(PyOrderedDictEntry, newsize);
if (newtable == NULL) {
PyErr_NoMemory();
return -1;
}
newotablep = PyMem_NEW(PyOrderedDictEntry*, newsize);
if (newotablep == NULL) {
PyErr_NoMemory();
return -1;
}
}
/* Make the dict empty, using the new table. */
assert(newtable != oldtable);
assert(newotablep != oldotablep);
mp->ma_table = newtable;
mp->od_otablep = newotablep;
mp->ma_mask = newsize - 1;
memset(newtable, 0, sizeof(PyOrderedDictEntry) * newsize);
memcpy(newotablep, oldotablep, sizeof(PyOrderedDictEntry *) * mp->ma_used);
epp = mp->od_otablep;
j = mp->ma_used;
mp->ma_used = 0;
i = mp->od_fill;
mp->od_fill = 0;
/* Copy the data over; this is refcount-neutral for active entries;
dummy entries aren't copied over, of course */
for (epp = reusing_smalltable ? small_ocopyp: mp->od_otablep; j > 0; epp++, j--) {
insertdict_clean(mp, (*epp)->me_key, (long)(*epp)->me_hash,
(*epp)->me_value);
}
for (ep = oldtable; i > 0; ep++) {
if (ep->me_value != NULL) { /* active entry */
--i;
} else if (ep->me_key != NULL) { /* dummy entry */
--i;
assert(ep->me_key == dummy);
Py_DECREF(ep->me_key);
}
/* else key == value == NULL: nothing to do */
}
if (is_oldtable_malloced) {
PyMem_DEL(oldtable);
PyMem_DEL(oldotablep);
}
return 0;
}
/* Create a new dictionary pre-sized to hold an estimated number of elements.
Underestimates are okay because the dictionary will resize as necessary.
Overestimates just mean the dictionary will be more sparse than usual.
*/
PyAPI_FUNC(PyObject *)
_PyOrderedDict_NewPresized(Py_ssize_t minused)
{
PyObject *op = PyOrderedDict_New();
if (minused>5 && op != NULL && dictresize((PyOrderedDictObject *)op, minused) == -1) {
Py_DECREF(op);
return NULL;
}
return op;
}
/* Note that, for historical reasons, PyOrderedDict_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
* meant the key wasn't present, in reality it can mean that, or that an error
* (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.
*/
PyObject *
PyOrderedDict_GetItem(PyObject *op, PyObject *key)
{
Py_hash_t hash;
PyOrderedDictObject *mp = (PyOrderedDictObject *)op;
PyOrderedDictEntry *ep;
PyThreadState *tstate;
if (!PyOrderedDict_Check(op))
return NULL;
if (!PyUNISTR_CheckExact(key) ||
(hash = ((PyUNISTR_Object *) key)->OB_HASH) == -1) {
hash = PyObject_Hash(key);
if (hash == -1) {
PyErr_Clear();
return NULL;
}
}
/* We can arrive here with a NULL tstate during initialization: try
running "python -Wi" for an example related to string interning.
Let's just hope that no exception occurs then... This must be
_PyThreadState_Current and not PyThreadState_GET() because in debug
mode, the latter complains if tstate is NULL. */
#if PY_VERSION_HEX < 0x03000000
tstate = _PyThreadState_Current;
#else
tstate = (PyThreadState*)_Py_atomic_load_relaxed(
&_PyThreadState_Current);
#endif
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;
}
static int
dict_set_item_by_hash_or_entry(register PyObject *op, PyObject *key,
Py_hash_t hash, PyOrderedDictEntry *ep, PyObject *value)
{
register PyOrderedDictObject *mp;
register Py_ssize_t n_used;
mp = (PyOrderedDictObject *)op;
assert(mp->od_fill <= mp->ma_mask); /* at least one empty slot */
n_used = mp->ma_used;
Py_INCREF(value);
Py_INCREF(key);
#if PY_MAJOR_VERSION < 3
if (PySortedDict_Check(op)) {
#else
if (PySortedDict_CheckExact(op)) {
#endif
if (insertsorteddict(mp, key, hash, value) != 0)
return -1;
} else if (insertdict(mp, key, hash, value, KVIO(mp) ? -2: -1) != 0)
return -1;
/* If we added a key, we can safely resize. Otherwise just return!
* If fill >= 2/3 size, adjust size. Normally, this doubles or
* quaduples the size, but it's also possible for the dict to shrink
* (if od_fill is much larger than ma_used, meaning a lot of dict
* keys have been * deleted).
*
* Quadrupling the size improves average dictionary sparseness
* (reducing collisions) at the cost of some memory and iteration
* speed (which loops over every possible entry). It also halves
* the number of expensive resize operations in a growing dictionary.
*
* Very large dictionaries (over 50K items) use doubling instead.
* This may help applications with severe memory constraints.
*/
if (!(mp->ma_used > n_used && mp->od_fill*3 >= (mp->ma_mask+1)*2))
return 0;
return dictresize(mp, (mp->ma_used > 50000 ? 2 : 4) * mp->ma_used);
}
/* CAUTION: PyOrderedDict_SetItem() must guarantee that it won't resize the
* dictionary if it's merely replacing the value for an existing key.
* This means that it's safe to loop over a dictionary with PyOrderedDict_Next()
* and occasionally replace a value -- but you can't insert new keys or
* remove them.
* This does never hold for kvio
*/
int
PyOrderedDict_SetItem(register PyObject *op, PyObject *key, PyObject *value)
{
register Py_hash_t hash;
if (!PyOrderedDict_Check(op)) {
PyErr_BadInternalCall();
return -1;
}
assert(key);
assert(value);
if (PyUNISTR_CheckExact(key)) {
hash = ((PyUNISTR_Object *)key)->OB_HASH;
if (hash == -1)
hash = PyObject_Hash(key);
} else {
hash = PyObject_Hash(key);
if (hash == -1)
return -1;
}
return dict_set_item_by_hash_or_entry(op, key, hash, NULL, value);
}
int
PyOrderedDict_InsertItem(register PyOrderedDictObject *mp, Py_ssize_t index,
PyObject *key, PyObject *value)
{
register Py_hash_t hash;
register Py_ssize_t n_used;
#if PY_MAJOR_VERSION < 3
if (PySortedDict_Check(mp)) {
#else
if (PySortedDict_CheckExact(mp)) {
#endif
PyErr_SetString(PyExc_TypeError,
"sorteddict does not support insert()");
return -1;
}
if (!PyOrderedDict_Check(mp)) {
PyErr_BadInternalCall();
return -1;
}
assert(key);
assert(value);
if (index < 0)
index += mp->ma_used;
/* test to see if index is in range */
if (index > mp->ma_used)
index = mp->ma_used;
else if (index < 0)
index = 0;
if (PyUNISTR_CheckExact(key)) {
hash = ((PyUNISTR_Object *)key)->OB_HASH;
if (hash == -1)
hash = PyObject_Hash(key);
} else {
hash = PyObject_Hash(key);
if (hash == -1)
return -1;
}
assert(mp->od_fill <= mp->ma_mask); /* at least one empty slot */
n_used = mp->ma_used;
Py_INCREF(value);
Py_INCREF(key);
if (insertdict(mp, key, hash, value, index) != 0)
return -1;
/* If we added a key, we can safely resize. Otherwise just return!
* If fill >= 2/3 size, adjust size. Normally, this doubles or
* quaduples the size, but it's also possible for the dict to shrink
* (if od_fill is much larger than ma_used, meaning a lot of dict
* keys have been * deleted).
*
* Quadrupling the size improves average dictionary sparseness
* (reducing collisions) at the cost of some memory and iteration
* speed (which loops over every possible entry). It also halves
* the number of expensive resize operations in a growing dictionary.
*
* Very large dictionaries (over 50K items) use doubling instead.
* This may help applications with severe memory constraints.
*/
if (!(mp->ma_used > n_used && mp->od_fill*3 >= (mp->ma_mask+1)*2))
return 0;
return dictresize(mp, (mp->ma_used > 50000 ? 2 : 4) * mp->ma_used);
}
static int
del_inorder(PyOrderedDictObject *op, PyOrderedDictEntry* ep)
{
register Py_ssize_t count = op->ma_used;
PyOrderedDictEntry **tmp = op->od_otablep;
while (count--) {
if (*tmp == ep) {
memmove(tmp, tmp+1, count * sizeof(PyOrderedDictEntry *));
return 1;
}
tmp++;
}
return 0; /* not found */
}
int
PyOrderedDict_DelItem(PyObject *op, PyObject *key)
{
register PyOrderedDictObject *mp;
register Py_hash_t hash;
register PyOrderedDictEntry *ep;
PyObject *old_value, *old_key;
if (!PyOrderedDict_Check(op)) {
PyErr_BadInternalCall();
return -1;
}
assert(key);
if (!PyUNISTR_CheckExact(key) ||
(hash = ((PyUNISTR_Object *) key)->OB_HASH) == -1) {
hash = PyObject_Hash(key);
if (hash == -1)
return -1;
}
mp = (PyOrderedDictObject *)op;
ep = (mp->ma_lookup)(mp, key, hash);
/* at this point we have to move all the entries beyond the one found
back on space (this could be optimised by deferring) */
del_inorder(mp, ep);
if (ep == NULL)
return -1;
if (ep->me_value == NULL) {
set_key_error(key);
return -1;
}
old_key = ep->me_key;
assert(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_value);
Py_DECREF(old_key);
return 0;
}
void
PyOrderedDict_Clear(PyObject *op)
{
PyOrderedDictObject *mp;
PyOrderedDictEntry *ep, *table, **otablep;
int table_is_malloced;
Py_ssize_t fill;
PyOrderedDictEntry small_copy[PyOrderedDict_MINSIZE];
#ifdef Py_DEBUG
Py_ssize_t i, n;
#endif
if (!PyOrderedDict_Check(op))
return;
mp = (PyOrderedDictObject *)op;
#ifdef Py_DEBUG
n = mp->ma_mask + 1;
i = 0;
#endif
table = mp->ma_table;
otablep = mp->od_otablep;
assert(table != NULL);
assert(otablep != 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->od_fill;
if (table_is_malloced)
EMPTY_TO_MINSIZE(mp);
else if (fill > 0) {
/* It's a small table with something that needs to be cleared.
* Afraid the only safe way is to copy the dict entries into
* another small table first.
*/
memcpy(small_copy, table, sizeof(small_copy));
table = small_copy;
EMPTY_TO_MINSIZE(mp);
}
/* else it's a small table that's already empty */
/* Now we can finally clear things. If C had refcounts, we could
* assert that the refcount on table is 1 now, i.e. that this function
* has unique access to it, so decref side-effects can't alter it.
*/
for (ep = table; fill > 0; ++ep) {
#ifdef Py_DEBUG
assert(i < n);
++i;
#endif
if (ep->me_key) {
--fill;
Py_DECREF(ep->me_key);
Py_XDECREF(ep->me_value);
}
#ifdef Py_DEBUG
else
assert(ep->me_value == NULL);
#endif
}
if (table_is_malloced) {
PyMem_DEL(table);
PyMem_DEL(otablep);
}
}
/*
* Iterate over a dict. Use like so:
*
* Py_ssize_t i;
* PyObject *key, *value;
* i = 0; # important! i should not otherwise be changed by you
* while (PyOrderedDict_Next(yourdict, &i, &key, &value)) {
* Refer to borrowed references in key and value.
* }
*
* CAUTION: In general, it isn't safe to use PyOrderedDict_Next in a loop that
* mutates the dict. One exception: it is safe if the loop merely changes
* the values associated with the keys (but doesn't insert new keys or
* delete keys), via PyOrderedDict_SetItem().
*/
int
PyOrderedDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue)
{
register Py_ssize_t i;
register PyOrderedDictEntry **epp;
if (!PyOrderedDict_Check(op) && !PySortedDict_Check(op))
return 0;
i = *ppos;
if (i < 0)
return 0;
/* review: not sure why different from 2.5.1 here. */
if (i >= ((PyOrderedDictObject *)op)->ma_used)
return 0;
*ppos = i+1;
epp = ((PyOrderedDictObject *)op)->od_otablep;
if (pkey)
*pkey = epp[i]->me_key;
if (pvalue)
*pvalue = epp[i]->me_value;
return 1;
}
/* Internal version of PyOrderedDict_Next that returns a hash value in addition to the key and value.*/
int
_PyOrderedDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue, Py_hash_t *phash)
{
register Py_ssize_t i;
register Py_ssize_t mask;
register PyOrderedDictEntry *ep;
if (!PyOrderedDict_Check(op))
return 0;
i = *ppos;
if (i < 0)
return 0;
ep = ((PyOrderedDictObject *)op)->ma_table;
mask = ((PyOrderedDictObject *)op)->ma_mask;
while (i <= mask && ep[i].me_value == NULL)
i++;
*ppos = i+1;
if (i > mask)
return 0;
*phash = (long)(ep[i].me_hash);
if (pkey)
*pkey = ep[i].me_key;
if (pvalue)
*pvalue = ep[i].me_value;
return 1;
}
/* Methods */
static void
dict_dealloc(register PyOrderedDictObject *mp)
{
register PyOrderedDictEntry *ep;
Py_ssize_t fill = mp->od_fill;
PyObject_GC_UnTrack(mp);
Py_TRASHCAN_SAFE_BEGIN(mp)
for (ep = mp->ma_table; fill > 0; ep++) {
if (ep->me_key) {
--fill;
Py_DECREF(ep->me_key);
Py_XDECREF(ep->me_value);
}
}
if (mp->ma_table != mp->ma_smalltable) {
PyMem_DEL(mp->ma_table);
PyMem_DEL(mp->od_otablep);
}
if (numfree < PyDict_MAXFREELIST && Py_TYPE(mp) == &PyOrderedDict_Type)
free_list[numfree++] = mp;
else
Py_TYPE(mp)->tp_free((PyObject *)mp);
Py_TRASHCAN_SAFE_END(mp)
}
#if PY_MAJOR_VERSION < 3
static int
ordereddict_print(register PyOrderedDictObject *mp, register FILE *fp, register int flags)
{
register Py_ssize_t i;
register Py_ssize_t any;
char *typestr = "ordered";
int status;
PyOrderedDictEntry **epp;
if (PySortedDict_CheckExact(mp))
typestr = "sorted";
status = Py_ReprEnter((PyObject*)mp);
if (status != 0) {
if (status < 0)
return status;
Py_BEGIN_ALLOW_THREADS
fprintf(fp, "%sdict([...])", typestr);
Py_END_ALLOW_THREADS
return 0;
}
Py_BEGIN_ALLOW_THREADS
fprintf(fp, "%sdict([", typestr);
Py_END_ALLOW_THREADS
any = 0;
epp = mp->od_otablep;
for (i = 0; i < mp->ma_used; i++) {
PyObject *pvalue = (*epp)->me_value;
/* Prevent PyObject_Repr from deleting value during
key format */
Py_INCREF(pvalue);
if (any++ > 0)
Py_BEGIN_ALLOW_THREADS
fprintf(fp, ", ");
Py_END_ALLOW_THREADS
Py_BEGIN_ALLOW_THREADS
fprintf(fp, "(");
Py_END_ALLOW_THREADS
if (PyObject_Print((PyObject *)((*epp)->me_key), fp, 0)!=0) {
Py_DECREF(pvalue);
Py_ReprLeave((PyObject*)mp);
return -1;
}
Py_BEGIN_ALLOW_THREADS
fprintf(fp, ", ");
Py_END_ALLOW_THREADS
if (PyObject_Print(pvalue, fp, 0) != 0) {
Py_DECREF(pvalue);
Py_ReprLeave((PyObject*)mp);
return -1;
}
Py_DECREF(pvalue);
Py_BEGIN_ALLOW_THREADS
fprintf(fp, ")");
Py_END_ALLOW_THREADS
epp++;
}
Py_BEGIN_ALLOW_THREADS
fprintf(fp, "])");
Py_END_ALLOW_THREADS
Py_ReprLeave((PyObject*)mp);
return 0;
}
#endif
static PyObject *
basedict_repr(PyOrderedDictObject *mp, char *typestr)
{
Py_ssize_t i;
PyObject *s, *temp, *comma = NULL, *rightpar = NULL;
PyObject *pieces = NULL, *result = NULL;
PyObject *key, *value;
/* char *typestr = "ordered"; */
/* if (PySortedDict_CheckExact(mp))*/
/*
#if PY_MAJOR_VERSION < 3
if (PySortedDict_Check(mp))
#else
if (PySortedDict_Check(mp))
#endif
typestr = "sorted";
*/
i = Py_ReprEnter((PyObject *)mp);
if (i != 0) {
return i > 0 ? PyUNISTR_FromFormat("%sdict([...])", typestr) : NULL;
}
if (mp->ma_used == 0) {
result = PyUNISTR_FromFormat("%sdict([])", typestr);
goto Done;
}
pieces = PyList_New(0);
if (pieces == NULL)
goto Done;
comma = PyUNISTR_FromString(", ");
if (comma == NULL)
goto Done;
rightpar = PyUNISTR_FromString(")");
if (rightpar == NULL)
goto Done;
/* Do repr() on each key+value pair, and insert ": " between them.
Note that repr may mutate the dict. */
i = 0;
while (PyOrderedDict_Next((PyObject *)mp, &i, &key, &value)) {
int status;
/* Prevent repr from deleting value during key format. */
Py_INCREF(value);
s = PyUNISTR_FromString("(");
PyUNISTR_ConcatAndDel(&s, PyObject_Repr(key));
PyUNISTR_Concat(&s, comma);
PyUNISTR_ConcatAndDel(&s, PyObject_Repr(value));
Py_DECREF(value);
PyUNISTR_Concat(&s, rightpar);
if (s == NULL)
goto Done;
status = PyList_Append(pieces, s);
Py_DECREF(s); /* append created a new ref */
if (status < 0)
goto Done;
}
/* Add "[]" decorations to the first and last items. */
assert(PyList_GET_SIZE(pieces) > 0);
s = PyUNISTR_FromFormat("%sdict([", typestr);
if (s == NULL)
goto Done;
temp = PyList_GET_ITEM(pieces, 0);
PyUNISTR_ConcatAndDel(&s, temp);
PyList_SET_ITEM(pieces, 0, s);
if (s == NULL)
goto Done;
s = PyUNISTR_FromString("])");
if (s == NULL)
goto Done;
temp = PyList_GET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1);
PyUNISTR_ConcatAndDel(&temp, s);
PyList_SET_ITEM(pieces, PyList_GET_SIZE(pieces) - 1, temp);
if (temp == NULL)
goto Done;
/* Paste them all together with ", " between. */
result = PyUNISTR_Join(comma, pieces);
Done:
Py_XDECREF(pieces);
Py_XDECREF(comma);
Py_XDECREF(rightpar);
Py_ReprLeave((PyObject *)mp);
return result;
}
static PyObject *
ordereddict_repr(PyOrderedDictObject *mp)
{
return basedict_repr(mp, "ordered");
}
static PyObject *
sorteddict_repr(PySortedDictObject *mp)
{
return basedict_repr((PyOrderedDictObject *)mp, "sorted");
}
static Py_ssize_t
dict_length(PyOrderedDictObject *mp)
{
return mp->ma_used;
}
static PyObject *
dict_subscript(PyOrderedDictObject *mp, register PyObject *key)
{
PyObject *v;
Py_hash_t hash;
PyOrderedDictEntry *ep;
if (PySlice_Check(key)) {
Py_ssize_t start, stop, step, slicelength;
PyObject* result;
if (PySlice_GetIndicesEx(
#if PY_VERSION_HEX < 0x03000000
(PySliceObject*)
#endif
key, mp->ma_used,
&start, &stop, &step, &slicelength) < 0) {
return NULL;
}
result = PyOrderedDict_New();
if (!result) return NULL;
if (slicelength <= 0) return result;
if (PyOrderedDict_CopySome(result, (PyObject *) mp, start, step, slicelength, 1) == 0)
return result;
Py_DECREF(result);
return NULL;
}
assert(mp->ma_table != NULL);
if (!PyUNISTR_CheckExact(key) ||
(hash = ((PyUNISTR_Object *) key)->OB_HASH) == -1) {
hash = PyObject_Hash(key);
if (hash == -1)
return NULL;
}
ep = (mp->ma_lookup)(mp, key, hash);
if (ep == NULL)
return NULL;
v = ep->me_value;
if (v == NULL) {
if (!PyOrderedDict_CheckExact(mp) && !PySortedDict_CheckExact(mp)) {
/* Look up __missing__ method if we're a subclass. */
#if PY_VERSION_HEX < 0x02070000
PyObject *missing;
static PyObject *missing_str = NULL;
if (missing_str == NULL)
missing_str =
PyString_InternFromString("__missing__");
missing = _PyType_Lookup(Py_TYPE(mp), missing_str);
if (missing != NULL)
return PyObject_CallFunctionObjArgs(missing,
(PyObject *)mp, key, NULL);
#else
PyObject *missing, *res;
static PyObject *missing_str = NULL;
missing = _PyObject_LookupSpecial((PyObject *)mp,
"__missing__",
&missing_str);
if (missing != NULL) {
res = PyObject_CallFunctionObjArgs(missing,
key, NULL);
Py_DECREF(missing);
return res;
}
else if (PyErr_Occurred())
return NULL;
#endif
}
set_key_error(key);
return NULL;
} else
Py_INCREF(v);
return v;
}
/* a[ilow:ihigh] = v if v != NULL.
* del a[ilow:ihigh] if v == NULL.
*
* Special speed gimmick: when v is NULL and ihigh - ilow <= 8, it's
* guaranteed the call cannot fail.
*/
static Py_ssize_t
dict_ass_slice(PyOrderedDictObject *self, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *value)
{
PyObject *recycle_on_stack[8];
PyObject **recycle = recycle_on_stack; /* will allocate more if needed */
Py_ssize_t result = -1, i;
Py_ssize_t num_to_delete = 0, s;
PyOrderedDictEntry **epp;
if (PySortedDict_CheckExact(self)) {
PyErr_Format(PyExc_TypeError,
"sorteddict does not support slice %s", value ? "assignment" : "deletion");
return -1;
}
if (ilow < 0)
ilow = 0;
else if (ilow > self->ma_used)
ilow = self->ma_used;
if (ihigh < ilow)
ihigh = ilow;
else if (ihigh > self->ma_used)
ihigh = self->ma_used;
if (value != NULL) {
if (PyObject_Length(value) != (ihigh - ilow)) {
PyErr_SetString(PyExc_ValueError,
"slice assignment: wrong size"
);
return -1;
}
if (!PyOrderedDict_CheckExact(value)) {
PyErr_SetString(PyExc_TypeError,
"slice assignment: argument must be ordereddict"
);
return -1;
}
}
/* for now lazy implementation: first delete then insert */
#define DELETION_AND_OVERWRITING_SEPERATE 0
#if DELETION_AND_OVERWRITING_SEPERATE == 1
if (value == NULL) {
#endif
s = (ihigh - ilow) * 2 * sizeof(PyObject *);
if (s > sizeof(recycle_on_stack)) {
recycle = (PyObject **)PyMem_MALLOC(s);
if (recycle == NULL) {
PyErr_NoMemory();
goto Error;
}
}
epp = self->od_otablep;
epp += ilow;
for (i = ilow; i < ihigh; i++, epp++) {
/* AvdN: ToDo DECREF key and value */
recycle[num_to_delete++] = (*epp)->me_key;
Py_INCREF(dummy);
(*epp)->me_key = dummy;
recycle[num_to_delete++] = (*epp)->me_value;
(*epp)->me_value = NULL;
}
epp = self->od_otablep;
memmove(epp+ilow, epp+ihigh, (self->ma_used - ihigh) * sizeof(PyOrderedDictEntry *));
self->ma_used -= (ihigh - ilow);
result = 0;
#if DELETION_AND_OVERWRITING_SEPERATE == 1
} else {
/* assignment first delete slice */
/* then delete any items whose keys are in itereable that are already in */
PyErr_SetString(PyExc_NotImplementedError,
"ordered dictionary does not support slice assignment"
);
result = -1;
}
#endif
for (i = num_to_delete - 1; i >= 0; --i)
Py_XDECREF(recycle[i]);
#if DELETION_AND_OVERWRITING_SEPERATE != 1
if (value != NULL) { /* now insert */
epp = ((PyOrderedDictObject *) value)->od_otablep;
for (i = ilow; i < ihigh; i++) {
if(PyOrderedDict_InsertItem(self, i, (*epp)->me_key, (*epp)->me_value) != 0)
return -1;
epp++;
}
}
#endif
Error:
if (recycle != recycle_on_stack)
PyMem_FREE(recycle);
return result;
}
static Py_ssize_t
dict_ass_subscript(PyOrderedDictObject *self, PyObject *item, PyObject *value)
{
if (PySlice_Check(item)) {
Py_ssize_t start, stop, step, slicelength;
if (PySortedDict_CheckExact(self)) {
PyErr_Format(PyExc_TypeError,
"sorteddict does not support slice %s", value ? "assignment" : "deletion");
return -1;
}
if (PySlice_GetIndicesEx(
#if PY_VERSION_HEX < 0x03000000
(PySliceObject*)
#endif
item, self->ma_used,
&start, &stop, &step, &slicelength) < 0) {
return -1;
}
/* treat L[slice(a,b)] = v _exactly_ like L[a:b] = v */
if (step == 1 && ((PySliceObject*)item)->step == Py_None)
return dict_ass_slice(self, start, stop, value);
/* do soemthing about step == -1 ? */
if (slicelength <= 0)
return 0;
if (value == NULL) {
/* delete slice */
/* printf("Deleting %d %d %d %d %p\n", start, stop, step, slicelength, value);*/
while (slicelength--) {
/* ToDo optimize */
if (step > 0) { /* do it from the back to preserve right indices */
dict_ass_slice(self, start + slicelength * step,
start + (slicelength * step) + 1, NULL);
} else {
dict_ass_slice(self, start,
start + 1, NULL);
start += step;
}
}
return 0;
} else {
/* assign slice */
Py_ssize_t count = slicelength, start2 = start;
PyOrderedDictEntry **epp;
/* printf("Assigning %d %d %d %d %d %p\n", start, stop, step, slicelength, PyObject_Length(value), value); */
if (PyObject_Length(value) != slicelength) {
PyErr_SetString(PyExc_ValueError,
"slice assignment: wrong size"
);
return -1;
}
if (!PyOrderedDict_CheckExact(value)) {
PyErr_SetString(PyExc_TypeError,
"slice assignment: argument must be ordereddict"
);
return -1;
}
while (count--) {
/* ToDo optimize */
if (step > 0) { /* do it from the back to preserve right indices */
dict_ass_slice(self, start2 + count * step,
start2 + (count * step) + 1, NULL);
} else {
dict_ass_slice(self, start2, start2 + 1, NULL);
start2 += step;
}
}
count = slicelength;
start2 = start;
epp = ((PyOrderedDictObject *) value)->od_otablep;
if (step < 0) {
epp += slicelength;
}
while (count--) {
/* ToDo optimize */
if (step > 0) { /* do it from the front */
if(PyOrderedDict_InsertItem(self, start2, (*epp)->me_key, (*epp)->me_value) != 0)
return -1;
start2 += step;
epp++;
} else {
epp--;
if(PyOrderedDict_InsertItem(self, start2 + count * step, (*epp)->me_key, (*epp)->me_value) != 0)
return -1;
}
}
return 0;
}
}
if (value == NULL)
return PyOrderedDict_DelItem((PyObject *)self, item);
else
return PyOrderedDict_SetItem((PyObject *)self, item, value);
}
static PyMappingMethods dict_as_mapping = {
(lenfunc)dict_length, /*mp_length*/
(binaryfunc)dict_subscript, /*mp_subscript*/
(objobjargproc)dict_ass_subscript, /*mp_ass_subscript*/
};
static PyObject *
dict_keys(register PyOrderedDictObject *mp, PyObject *args, PyObject *kwds)
{
register PyObject *v;
register Py_ssize_t i;
PyOrderedDictEntry **epp;
Py_ssize_t n;
int reverse = 0;
static char *kwlist[] = {"reverse", 0};
if (args != NULL)
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:keys",
kwlist, &reverse))
return NULL;
again:
n = mp->ma_used;
v = PyList_New(n);
if (v == NULL)
return NULL;
if (n != mp->ma_used) {
/* Durnit. The allocations caused the dict to resize.
* Just over, this shouldn't normally happen.
*/
Py_DECREF(v);
goto again;
}
if (reverse) {
epp = mp->od_otablep + (n-1);
reverse = -1;
} else {
epp = mp->od_otablep;
reverse = 1;
}
for (i = 0; i < n; i++) {
PyObject *key = (*epp)->me_key;
Py_INCREF(key);
PyList_SET_ITEM(v, i, key);
epp += reverse;
}
return v;
}
static PyObject *
dict_values(register PyOrderedDictObject *mp, PyObject *args, PyObject *kwds)
{
register PyObject *v;
register Py_ssize_t i;
PyOrderedDictEntry **epp;
Py_ssize_t n;
int reverse = 0;
static char *kwlist[] = {"reverse", 0};
if (args != NULL)
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:values",
kwlist, &reverse))
return NULL;
again:
n = mp->ma_used;
v = PyList_New(n);
if (v == NULL)
return NULL;
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;
}
if (reverse) {
epp = mp->od_otablep + (n-1);
reverse = -1;
} else {
epp = mp->od_otablep;
reverse = 1;
}
for (i = 0; i < n; i++) {
PyObject *value = (*epp)->me_value;
Py_INCREF(value);
PyList_SET_ITEM(v, i, value);
epp += reverse;
}
return v;
}
static PyObject *
dict_items(register PyOrderedDictObject *mp, PyObject *args, PyObject *kwds)
{
register PyObject *v;
register Py_ssize_t i, n;
PyObject *item, *key, *value;
PyOrderedDictEntry **epp;
int reverse = 0;
static char *kwlist[] = {"reverse", 0};
if (args != NULL)
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:items",
kwlist, &reverse))
return NULL;
/* 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);
if (v == NULL)
return NULL;
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. */
if (reverse) {
epp = mp->od_otablep + (n-1);
reverse = -1;
} else {
epp = mp->od_otablep;
reverse = 1;
}
for (i = 0; i < n; i++) {
key = (*epp)->me_key;
value = (*epp)->me_value;
item = PyList_GET_ITEM(v, i);
Py_INCREF(key);
PyTuple_SET_ITEM(item, 0, key);
Py_INCREF(value);
PyTuple_SET_ITEM(item, 1, value);
epp += reverse;
}
return v;
}
static PyObject *
dict_fromkeys(PyObject *cls, PyObject *args)
{
PyObject *seq;
PyObject *value = Py_None;
PyObject *it; /* iter(seq) */
PyObject *key;
PyObject *d;
int status;
if (!PyArg_UnpackTuple(args, "fromkeys", 1, 2, &seq, &value))
return NULL;
d = PyObject_CallObject(cls, NULL);
if (d == NULL)
return NULL;
if ((PyOrderedDict_CheckExact(d) || PySortedDict_CheckExact(d)) && ((PyDictObject *)d)->ma_used == 0) {
if (PyAnySet_CheckExact(seq)) {
PyOrderedDictObject *mp = (PyOrderedDictObject *)d;
Py_ssize_t pos = 0;
PyObject *key;
Py_hash_t hash;
if (dictresize(mp, PySet_GET_SIZE(seq))) {
Py_DECREF(d);
return NULL;
}
while (_PySet_NextEntry(seq, &pos, &key, &hash)) {
Py_INCREF(key);
Py_INCREF(value);
if (insertdict(mp, key, hash, value, -1)) {
Py_DECREF(d);
return NULL;
}
}
return d;
}
}
it = PyObject_GetIter(seq);
if (it == NULL) {
Py_DECREF(d);
return NULL;
}
#ifndef OLD
if (PyOrderedDict_CheckExact(d) || PySortedDict_CheckExact(d)) {
while ((key = PyIter_Next(it)) != NULL) {
status = PyOrderedDict_SetItem(d, key, value);
Py_DECREF(key);
if (status < 0)
goto Fail;
}
} else {
while ((key = PyIter_Next(it)) != NULL) {
status = PyObject_SetItem(d, key, value);
Py_DECREF(key);
if (status < 0)
goto Fail;
}
}
if (PyErr_Occurred())
goto Fail;
#else
for (;;) {
key = PyIter_Next(it);
if (key == NULL) {
if (PyErr_Occurred())
goto Fail;
break;
}
status = PyObject_SetItem(d, key, value);
Py_DECREF(key);
if (status < 0)
goto Fail;
}
#endif
Py_DECREF(it);
return d;
Fail:
Py_DECREF(it);
Py_DECREF(d);
return NULL;
}
/* called by init, update and setitems */
static int
dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, char *args_name)
{
PyObject *arg = NULL;
int result = 0, tmprelax = 0;
static char *kwlist[] = {"src", "relax", 0};
if (args != NULL)
if (!PyArg_ParseTupleAndKeywords(args, kwds, args_name,
kwlist, &arg, &tmprelax))
return -1;
if (arg != NULL) {
if (PyObject_HasAttrString(arg, "keys"))
result = PyOrderedDict_Merge(self, arg, 1, tmprelax);
else
result = PyOrderedDict_MergeFromSeq2(self, arg, 1);
}
/* do not initialise from keywords at all */
/* if (result == 0 && kwds != NULL)
result = PyOrderedDict_Merge(self, kwds, 1); */
return result;
}
static PyObject *
dict_update(PyObject *self, PyObject *args, PyObject *kwds)
{
if (dict_update_common(self, args, kwds, "|Oi:update") != -1)
Py_RETURN_NONE;
return NULL;
}
/* Update unconditionally replaces existing items.
Merge has a 3rd argument 'override'; if set, it acts like Update,
otherwise it leaves existing items unchanged.
PyOrderedDict_{Update,Merge} update/merge from a mapping object.
PyOrderedDict_MergeFromSeq2 updates/merges from any iterable object
producing iterable objects of length 2.
*/
int
PyOrderedDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override)
{
PyObject *it; /* iter(seq2) */
Py_ssize_t i; /* index into seq2 of current element */
PyObject *item; /* seq2[i] */
PyObject *fast; /* item as a 2-tuple or 2-list */
assert(d != NULL);
assert(PyOrderedDict_Check(d));
assert(seq2 != NULL);
it = PyObject_GetIter(seq2);
if (it == NULL)
return -1;
for (i = 0; ; ++i) {
PyObject *key, *value;
Py_ssize_t n;
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 "
"sequence element #%zd to a sequence",
i);
goto Fail;
}
n = PySequence_Fast_GET_SIZE(fast);
if (n != 2) {
PyErr_Format(PyExc_ValueError,
"dictionary update sequence element #%zd "
"has length %zd; 2 is required",
i, n);
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 || PyOrderedDict_GetItem(d, key) == NULL) {
int status = PyOrderedDict_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);
return Py_SAFE_DOWNCAST(i, Py_ssize_t, int);
}
int
PyOrderedDict_Update(PyObject *a, PyObject *b)
{
return PyOrderedDict_Merge(a, b, 1, 0);
}
int
PyOrderedDict_Merge(PyObject *a, PyObject *b, int override, int relaxed)
{
register PyOrderedDictObject *mp, *other;
register Py_ssize_t i;
PyOrderedDictEntry *entry, **epp;
/* We accept for the argument either a concrete ordered 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 (a == NULL || !PyOrderedDict_Check(a) || b == NULL) {
PyErr_BadInternalCall();
return -1;
}
mp = (PyOrderedDictObject*)a;
/* sorted dicts are always done with individual elements */
if (!PySortedDict_CheckExact(a) && PyOrderedDict_CheckExact(b)) {
other = (PyOrderedDictObject *) b;
if (other == mp || other->ma_used == 0)
/* a.update(a) or a.update({}); nothing to do */
return 0;
if (mp->ma_used == 0)
/* Since the target dict is empty, PyOrderedDict_GetItem()
* always returns NULL. Setting override to 1
* skips the unnecessary test.
*/
override = 1;
/* 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->od_fill + other->ma_used)*3 >= (mp->ma_mask+1)*2) {
if (dictresize(mp, (mp->ma_used + other->ma_used)*2) != 0)
return -1;
}
epp = other->od_otablep;
for (i = 0; i < other->ma_used; i++) {
entry = *epp++;
/* entry->me_value is never NULL when following the otablep */
/*
if (entry->me_value != NULL &&
(override ||
PyOrderedDict_GetItem(a, entry->me_key) == NULL)) {
*/
if (override || PyOrderedDict_GetItem(a, entry->me_key) == NULL) {
Py_INCREF(entry->me_key);
Py_INCREF(entry->me_value);
if (insertdict(mp, entry->me_key,
(long)entry->me_hash,
entry->me_value, -1) != 0)
return -1;
}
}
} else if (relaxed || RELAXED(mp)) {
/* Do it the generic, slower way */
PyObject *keys = PyMapping_Keys(b);
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.
*/
return -1;
iter = PyObject_GetIter(keys);
Py_DECREF(keys);
if (iter == NULL)
return -1;
for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) {
if (!override && PyDict_GetItem(a, key) != NULL) {
Py_DECREF(key);
continue;
}
value = PyObject_GetItem(b, key);
if (value == NULL) {
Py_DECREF(iter);
Py_DECREF(key);
return -1;
}
status = PyOrderedDict_SetItem(a, key, value);
Py_DECREF(key);
Py_DECREF(value);
if (status < 0) {
Py_DECREF(iter);
return -1;
}
}
Py_DECREF(iter);
if (PyErr_Occurred())
/* Iterator completed, via error */
return -1;
} else {
PyErr_SetString(PyExc_TypeError,
"source has undefined order");
return -1;
}
return 0;
}
/*
assume that the start step and count are all within the
borders of what b provides
*/
int
PyOrderedDict_CopySome(PyObject *a, PyObject *b,
Py_ssize_t start, Py_ssize_t step,
Py_ssize_t count, int override)
{
register PyOrderedDictObject *mp, *other;
register Py_ssize_t i;
PyOrderedDictEntry *entry, **epp;
/* We accept for the argument either a concrete ordered dictionary object
*/
if (a == NULL || !PyOrderedDict_Check(a) || b == NULL) {
PyErr_BadInternalCall();
return -1;
}
mp = (PyOrderedDictObject*)a;
if (PyOrderedDict_CheckExact(b) || PySortedDict_CheckExact(b)) {
other = (PyOrderedDictObject*)b;
if (other == mp || other->ma_used == 0)
/* a.update(a) or a.update({}); nothing to do */
return 0;
if (mp->ma_used == 0)
/* Since the target dict is empty, PyOrderedDict_GetItem()
* always returns NULL. Setting override to 1
* skips the unnecessary test.
*/
override = 1;
/* 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->od_fill + count)*3 >= (mp->ma_mask+1)*2) {
if (dictresize(mp, (mp->ma_used + count)*2) != 0)
return -1;
}
epp = other->od_otablep;
epp += start;
for (i = 0; i < count; i++, epp += step) {
entry = *epp;
if (override || PyOrderedDict_GetItem(a, entry->me_key) == NULL) {
Py_INCREF(entry->me_key);
Py_INCREF(entry->me_value);
if (insertdict(mp, entry->me_key,
(long)entry->me_hash,
entry->me_value, -1) != 0)
return -1;
}
}
} else {
PyErr_SetString(PyExc_TypeError,
"source has undefined order");
return -1;
}
return 0;
}
static PyObject *
dict_copy(register PyOrderedDictObject *mp)
{
return PyOrderedDict_Copy((PyObject*)mp);
}
PyObject *
PyOrderedDict_Copy(PyObject *o)
{
PyObject *copy;
if (o == NULL || !PyOrderedDict_Check(o)) {
PyErr_BadInternalCall();
return NULL;
}
if (PySortedDict_CheckExact(o)) {
copy = PySortedDict_New();
if (copy == NULL)
return NULL;
((PySortedDictObject *) copy)->sd_cmp = ((PySortedDictObject *) o)->sd_cmp;
((PySortedDictObject *) copy)->sd_key = ((PySortedDictObject *) o)->sd_key;
((PySortedDictObject *) copy)->sd_value = ((PySortedDictObject *) o)->sd_value;
} else {
copy = PyOrderedDict_New();
if (copy == NULL)
return NULL;
}
((PyOrderedDictObject *) copy)->od_state = ((PyOrderedDictObject *) o)->od_state;
if (PyOrderedDict_Merge(copy, o, 1, 0) == 0)
return copy;
Py_DECREF(copy);
return NULL;
}
Py_ssize_t
PyOrderedDict_Size(PyObject *mp)
{
if (mp == NULL || !PyOrderedDict_Check(mp)) {
PyErr_BadInternalCall();
return -1;
}
return ((PyOrderedDictObject *)mp)->ma_used;
}
PyObject *
PyOrderedDict_Keys(PyObject *mp)
{
if (mp == NULL || !PyOrderedDict_Check(mp)) {
PyErr_BadInternalCall();
return NULL;
}
return dict_keys((PyOrderedDictObject *)mp, NULL, NULL);
}
PyObject *
PyOrderedDict_Values(PyObject *mp)
{
if (mp == NULL || !PyOrderedDict_Check(mp)) {
PyErr_BadInternalCall();
return NULL;
}
return dict_values((PyOrderedDictObject *)mp, NULL, NULL);
}
PyObject *
PyOrderedDict_Items(PyObject *mp)
{
if (mp == NULL || !PyOrderedDict_Check(mp)) {
PyErr_BadInternalCall();
return NULL;
}
return dict_items((PyOrderedDictObject *)mp, NULL, NULL);
}
#if PY_VERSION_HEX < 0x03000000
/* Subroutine which returns the smallest key in a for which b's value
is different or absent. The value is returned too, through the
pval argument. Both are NULL if no key in a is found for which b's status
differs. The refcounts on (and only on) non-NULL *pval and function return
values must be decremented by the caller (characterize() increments them
to ensure that mutating comparison and PyOrderedDict_GetItem calls can't delete
them before the caller is done looking at them). */
static PyObject *
characterize(PyOrderedDictObject *a, PyOrderedDictObject *b, PyObject **pval)
{
PyObject *akey = NULL; /* smallest key in a s.t. a[akey] != b[akey] */
PyObject *aval = NULL; /* a[akey] */
Py_ssize_t i;
int cmp;
for (i = 0; i <= a->ma_mask; i++) {
PyObject *thiskey, *thisaval, *thisbval;
if (a->ma_table[i].me_value == NULL)
continue;
thiskey = a->ma_table[i].me_key;
Py_INCREF(thiskey); /* keep alive across compares */
if (akey != NULL) {
cmp = PyObject_RichCompareBool(akey, thiskey, Py_LT);
if (cmp < 0) {
Py_DECREF(thiskey);
goto Fail;
}
if (cmp > 0 ||
i > a->ma_mask ||
a->ma_table[i].me_value == NULL) {
/* Not the *smallest* a key; or maybe it is
* but the compare shrunk the dict so we can't
* find its associated value anymore; or
* maybe it is but the compare deleted the
* a[thiskey] entry.
*/
Py_DECREF(thiskey);
continue;
}
}
/* Compare a[thiskey] to b[thiskey]; cmp <- true iff equal. */
thisaval = a->ma_table[i].me_value;
assert(thisaval);
Py_INCREF(thisaval); /* keep alive */
thisbval = PyOrderedDict_GetItem((PyObject *)b, thiskey);
if (thisbval == NULL)
cmp = 0;
else {
/* both dicts have thiskey: same values? */
cmp = PyObject_RichCompareBool(
thisaval, thisbval, Py_EQ);
if (cmp < 0) {
Py_DECREF(thiskey);
Py_DECREF(thisaval);
goto Fail;
}
}
if (cmp == 0) {
/* New winner. */
Py_XDECREF(akey);
Py_XDECREF(aval);
akey = thiskey;
aval = thisaval;
} else {
Py_DECREF(thiskey);
Py_DECREF(thisaval);
}
}
*pval = aval;
return akey;
Fail:
Py_XDECREF(akey);
Py_XDECREF(aval);
*pval = NULL;
return NULL;
}
static int
dict_compare(PyOrderedDictObject *a, PyOrderedDictObject *b)
{
PyObject *adiff, *bdiff, *aval, *bval;
int res;
/* Compare lengths first */
if (a->ma_used < b->ma_used)
return -1; /* a is shorter */
else if (a->ma_used > b->ma_used)
return 1; /* b is shorter */
/* Same length -- check all keys */
bdiff = bval = NULL;
adiff = characterize(a, b, &aval);
if (adiff == NULL) {
assert(!aval);
/* Either an error, or a is a subset with the same length so
* must be equal.
*/
res = PyErr_Occurred() ? -1 : 0;
goto Finished;
}
bdiff = characterize(b, a, &bval);
if (bdiff == NULL && PyErr_Occurred()) {
assert(!bval);
res = -1;
goto Finished;
}
res = 0;
if (bdiff) {
/* bdiff == NULL "should be" impossible now, but perhaps
* the last comparison done by the characterize() on a had
* the side effect of making the dicts equal!
*/
res = PyObject_Compare(adiff, bdiff);
}
if (res == 0 && bval != NULL)
res = PyObject_Compare(aval, bval);
Finished:
Py_XDECREF(adiff);
Py_XDECREF(bdiff);
Py_XDECREF(aval);
Py_XDECREF(bval);
return res;
}
#endif
/* 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(PyOrderedDictObject *a, PyOrderedDictObject *b)
{
Py_ssize_t i;
PyOrderedDictEntry **app, **bpp;
if (a->ma_used != b->ma_used)
/* can't be equal if # of entries differ */
return 0;
/* Same # of entries -- check all of 'em. Exit early on any diff. */
for (i = 0, app = a->od_otablep, bpp = b->od_otablep; i < a->ma_used;
i++, app++, bpp++) {
int cmp;
PyObject *aval = (*app)->me_value;
PyObject *bval = (*bpp)->me_value;
PyObject *akey = (*app)->me_key;
PyObject *bkey = (*bpp)->me_key;
/* temporarily bump aval's refcount to ensure it stays
alive until we're done with it */
Py_INCREF(aval);
Py_INCREF(bval);
/* ditto for key */
Py_INCREF(akey);
Py_INCREF(bkey);
cmp = PyObject_RichCompareBool(akey, bkey, Py_EQ);
if (cmp > 0) /* keys compare ok, now do values */
cmp = PyObject_RichCompareBool(aval, bval, Py_EQ);
Py_DECREF(bkey);
Py_DECREF(akey);
Py_DECREF(bval);
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 (!PyOrderedDict_Check(v) || !PyOrderedDict_Check(w)) {
res = Py_NotImplemented;
} else if (op == Py_EQ || op == Py_NE) {
cmp = dict_equal((PyOrderedDictObject *)v, (PyOrderedDictObject *)w);
if (cmp < 0)
return NULL;
res = (cmp == (op == Py_EQ)) ? Py_True : Py_False;
} else {
#if PY_VERSION_HEX < 0x03000000
/* Py3K warning if comparison isn't == or != */
if (PyErr_WarnPy3k("dict inequality comparisons not supported "
"in 3.x", 1) < 0) {
return NULL;
}
#endif
res = Py_NotImplemented;
}
Py_INCREF(res);
return res;
}
static PyObject *
dict_contains(register PyOrderedDictObject *mp, PyObject *key)
{
Py_hash_t hash;
PyOrderedDictEntry *ep;
if (!PyUNISTR_CheckExact(key) ||
(hash = ((PyUNISTR_Object *) key)->OB_HASH) == -1) {
hash = PyObject_Hash(key);
if (hash == -1)
return NULL;
}
ep = (mp->ma_lookup)(mp, key, hash);
if (ep == NULL)
return NULL;
return PyBool_FromLong(ep->me_value != NULL);
}
#if PY_VERSION_HEX < 0x03000000
static PyObject *
dict_has_key(register PyOrderedDictObject *mp, PyObject *key)
{
if (Py_Py3kWarningFlag &&
PyErr_Warn(PyExc_DeprecationWarning,
"dict.has_key() not supported in 3.x") < 0)
return NULL;
return dict_contains(mp, key);
}
#endif
static PyObject *
dict_get(register PyOrderedDictObject *mp, PyObject *args)
{
PyObject *key;
PyObject *failobj = Py_None;
PyObject *val = NULL;
Py_hash_t hash;
PyOrderedDictEntry *ep;
if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &failobj))
return NULL;
if (!PyUNISTR_CheckExact(key) ||
(hash = ((PyUNISTR_Object *) key)->OB_HASH) == -1) {
hash = PyObject_Hash(key);
if (hash == -1)
return NULL;
}
ep = (mp->ma_lookup)(mp, key, hash);
if (ep == NULL)
return NULL;
val = ep->me_value;
if (val == NULL)
val = failobj;
Py_INCREF(val);
return val;
}
static PyObject *
dict_setdefault(register PyOrderedDictObject *mp, PyObject *args)
{
PyObject *key;
PyObject *failobj = Py_None;
PyObject *val = NULL;
Py_hash_t hash;
PyOrderedDictEntry *ep;
if (!PyArg_UnpackTuple(args, "setdefault", 1, 2, &key, &failobj))
return NULL;
if (!PyUNISTR_CheckExact(key) ||
(hash = ((PyUNISTR_Object *) key)->OB_HASH) == -1) {
hash = PyObject_Hash(key);
if (hash == -1)
return NULL;
}
ep = (mp->ma_lookup)(mp, key, hash);
if (ep == NULL)
return NULL;
val = ep->me_value;
if (val == NULL) {
if (dict_set_item_by_hash_or_entry((PyObject*)mp, key, hash, ep,
failobj) == 0)
val = failobj;
}
Py_XINCREF(val);
return val;
}
static PyObject *
dict_clear(register PyOrderedDictObject *mp)
{
PyOrderedDict_Clear((PyObject *)mp);
Py_RETURN_NONE;
}
static PyObject *
dict_pop(PyOrderedDictObject *mp, PyObject *args)
{
Py_hash_t hash;
PyOrderedDictEntry *ep;
PyObject *old_value, *old_key;
PyObject *key, *deflt = NULL;
if(!PyArg_UnpackTuple(args, "pop", 1, 2, &key, &deflt))
return NULL;
if (mp->ma_used == 0) {
if (deflt) {
Py_INCREF(deflt);
return deflt;
}
PyErr_SetString(PyExc_KeyError,
"pop(): dictionary is empty");
return NULL;
}
if (!PyUNISTR_CheckExact(key) ||
(hash = ((PyUNISTR_Object *) key)->OB_HASH) == -1) {
hash = PyObject_Hash(key);
if (hash == -1)
return NULL;
}
ep = (mp->ma_lookup)(mp, key, hash);
if (ep == NULL)
return NULL;
if (ep->me_value == NULL) {
if (deflt) {
Py_INCREF(deflt);
return deflt;
}
set_key_error(key);
return NULL;
}
old_key = ep->me_key;
Py_INCREF(dummy);
ep->me_key = dummy;
old_value = ep->me_value;
ep->me_value = NULL;
del_inorder(mp, ep);
mp->ma_used--;
Py_DECREF(old_key);
return old_value;
}
static PyObject *
dict_popitem(PyOrderedDictObject *mp, PyObject *args)
{
Py_hash_ssize_t i = -1, j;
PyOrderedDictEntry **epp;
PyObject *res;
/* 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
* tuple away if the dict *is* empty isn't a significant
* inefficiency -- possible, but unlikely in practice.
*/
if (!PyArg_ParseTuple(args, "|n:popitem", &i))
return NULL;
res = PyTuple_New(2);
if (res == NULL)
return NULL;
if (mp->ma_used == 0) {
Py_DECREF(res);
PyErr_SetString(PyExc_KeyError,
"popitem(): dictionary is empty");
return NULL;
}
if (i < 0)
j = mp->ma_used + i;
else
j = i;
if (j < 0 || j >= mp->ma_used) {
Py_DECREF(res);
PyErr_SetString(PyExc_KeyError,
"popitem(): index out of range");
return NULL;
}
epp = mp->od_otablep;
epp += j;
PyTuple_SET_ITEM(res, 0, (*epp)->me_key);
PyTuple_SET_ITEM(res, 1, (*epp)->me_value);
Py_INCREF(dummy);
(*epp)->me_key = dummy;
(*epp)->me_value = NULL;
mp->ma_used--;
if (i != -1) { /* for default case -1, we don't have to do anything */
/* ma_used has already been decremented ! */
memmove(epp, epp+1, (mp->ma_used - j) * sizeof(PyOrderedDictEntry *));
}
return res;
}
static int
dict_traverse(PyObject *op, visitproc visit, void *arg)
{
Py_ssize_t i = 0;
PyObject *pk;
PyObject *pv;
while (PyOrderedDict_Next(op, &i, &pk, &pv)) {
Py_VISIT(pk);
Py_VISIT(pv);
}
return 0;
}
static int
dict_tp_clear(PyObject *op)
{
PyOrderedDict_Clear(op);
return 0;
}
#if PY_MAJOR_VERSION < 3
extern PyTypeObject PyOrderedDictIterKey_Type; /* Forward */
extern PyTypeObject PyOrderedDictIterValue_Type; /* Forward */
extern PyTypeObject PyOrderedDictIterItem_Type; /* Forward */
#endif
static PyObject *dictiter_new(PyOrderedDictObject *, PyTypeObject *,
PyObject *args, PyObject *kwds);
#if PY_MAJOR_VERSION < 3
static PyObject *
dict_iterkeys(PyOrderedDictObject *dict, PyObject *args, PyObject *kwds)
{
return dictiter_new(dict, &PyOrderedDictIterKey_Type, args, kwds);
}
static PyObject *
dict_itervalues(PyOrderedDictObject *dict, PyObject *args, PyObject *kwds)
{
return dictiter_new(dict, &PyOrderedDictIterValue_Type, args, kwds);
}
static PyObject *
dict_iteritems(PyOrderedDictObject *dict, PyObject *args, PyObject *kwds)
{
return dictiter_new(dict, &PyOrderedDictIterItem_Type, args, kwds);
}
#endif
static PyObject *
dict_sizeof(PyDictObject *mp)
{
Py_ssize_t res;
res = sizeof(PyOrderedDictObject);
if (mp->ma_table != mp->ma_smalltable)
res = res + (mp->ma_mask + 1) * sizeof(PyOrderedDictEntry);
#if PY_VERSION_HEX < 0x03000000
return PyInt_FromSize_t(res);
#else
return PyLong_FromSize_t(res);
#endif
}
static PyObject *
dict_index(register PyOrderedDictObject *mp, PyObject *key)
{
Py_hash_t hash;
PyOrderedDictEntry *ep, **tmp;
register Py_ssize_t index;
if (!PyUNISTR_CheckExact(key) ||
(hash = ((PyUNISTR_Object *) key)->OB_HASH) == -1) {
hash = PyObject_Hash(key);
if (hash == -1)
return NULL;
}
ep = (mp->ma_lookup)(mp, key, hash);
if (ep == NULL || ep->me_value == NULL) {
PyErr_SetString(PyExc_ValueError,
"ordereddict.index(x): x not a key in ordereddict"
);
return NULL;
}
for (index = 0, tmp = mp->od_otablep; index < mp->ma_used; index++, tmp++) {
if (*tmp == ep) {
#if PY_VERSION_HEX < 0x03000000
return PyInt_FromSize_t(index);
#else
return PyLong_FromSize_t(index);
#endif
}
}
return NULL; /* not found */
}
static PyObject *
dict_insert(PyOrderedDictObject *mp, PyObject *args)
{
Py_ssize_t i;
PyObject *key;
PyObject *val;
#if PY_VERSION_HEX >= 0x02050000
if (!PyArg_ParseTuple(args, "nOO:insert", &i, &key, &val))
#else
if (!PyArg_ParseTuple(args, "iOO:insert", &i, &key, &val))
#endif
return NULL;
if(PyOrderedDict_InsertItem(mp, i, key, val) != 0)
return NULL;
Py_RETURN_NONE;
}
static PyObject *
dict_reverse(register PyOrderedDictObject *mp)
{
PyOrderedDictEntry **epps, **eppe, *tmp;
epps = mp->od_otablep;
eppe = epps + ((mp->ma_used)-1);
while (epps < eppe) {
tmp = *epps;
*epps++ = *eppe;
*eppe-- = tmp;
}
Py_RETURN_NONE;
}
static PyObject *
dict_setkeys(register PyOrderedDictObject *mp, PyObject *keys)
{
PyOrderedDictEntry **newtable, *item;
Py_ssize_t size = mp->ma_used * sizeof(PyOrderedDictEntry *), i, oldindex;
PyObject *key = NULL;
PyObject *it;
Py_hash_t hash;
if (PySortedDict_CheckExact(mp)) {
PyErr_SetString(PyExc_TypeError,
"sorteddict does not support setkeys() assignment");
return NULL;
}
/* determine length -> ok if ok
if ok, then we still don't know if all keys will be found
so we allocate an array of ma_mask+1 size (which is what was used for
last resize and start filling that.
On finish, memcopy (so we don't have to worry about where the
values actually are (allocated or in smallbuffer), and
delete the tmp stuff,
if some key cannot be found (or is double) we don't update
*/
newtable = PyMem_NEW(PyOrderedDictEntry *, size);
if (newtable == NULL) {
PyErr_NoMemory();
return NULL;
}
i = PyObject_Length(keys);
if ((i >=0) && (i != mp->ma_used)) {
PyErr_Format(PyExc_ValueError,
"ordereddict setkeys requires sequence of length #%zd; "
"provided was length %zd",
mp->ma_used, i);
return NULL;
}
if (i == -1) PyErr_Clear();
it = PyObject_GetIter(keys);
if (it == NULL)
return NULL;
for (i = 0; ; ++i) {
key = PyIter_Next(it);
if (key == NULL) {
if (PyErr_Occurred()) break;
if (i != mp->ma_used) {
PyErr_Format(PyExc_ValueError,
"ordereddict setkeys requires sequence of length #%zd; "
"provided was length %zd",
mp->ma_used, i);
break;
}
memcpy(mp->od_otablep, newtable, size);
PyMem_DEL(newtable);
Py_DECREF(it);
Py_RETURN_NONE;
}
if (i >= mp->ma_used) {
PyErr_Format(PyExc_ValueError,
"ordereddict setkeys requires sequence of max length #%zd; "
"a longer sequence was provided",
mp->ma_used);
Py_DECREF(it);
return NULL;
}
/* find the item with this key */
if (!PyUNISTR_CheckExact(key) ||
(hash = ((PyUNISTR_Object *) key)->OB_HASH) == -1) {
hash = PyObject_Hash(key);
if (hash == -1)
break;
}
item = (mp->ma_lookup)(mp, key, hash);
if (item == NULL || item->me_value == NULL) {
PyErr_Format(PyExc_KeyError,
"ordereddict setkeys unknown key at pos " SPR,
i);
break;
}
/* PyObject_Print((PyObject *)item->me_key, stdout, 0);*/
/* check if a pointer to this item has been set */
for (oldindex = 0; oldindex < i; oldindex++) {
if (newtable[oldindex] == item) {
PyErr_Format(PyExc_KeyError,
"ordereddict setkeys same key at pos " SPR "and " SPR,
oldindex, i);
break;
}
}
/* insert the pointer to this item */
newtable[i] = item;
}
PyMem_DEL(newtable);
Py_XDECREF(key);
Py_DECREF(it);
return NULL;
}
static PyObject *
dict_setvalues(register PyOrderedDictObject *mp, PyObject *values)
{
PyObject *it; /* iter(seq2) */
Py_ssize_t i; /* index into seq2 of current element */
PyObject *item = NULL; /* values[i] */
PyOrderedDictEntry **epp = mp->od_otablep, *tmp;
assert(mp != NULL);
assert(PyOrderedDict_Check(mp));
assert(values != NULL);
i = PyObject_Length(values);
/* printf("\nlength %d %d\n", i, mp->ma_used); */
if ((i >=0) && (i != mp->ma_used)) {
PyErr_Format(PyExc_ValueError,
"ordereddict setvalues requires sequence of length #%zd; "
"provided was length %zd",
mp->ma_used, i);
return NULL;
}
if (i == -1) PyErr_Clear();
it = PyObject_GetIter(values);
if (it == NULL)
return NULL;
for (i = 0; ; ++i) {
item = PyIter_Next(it);
if (item == NULL) {
if (PyErr_Occurred()) break;
if (i != mp->ma_used) {
PyErr_Format(PyExc_ValueError,
"ordereddict setvalues requires sequence of length #%zd; "
"provided was length %zd, ordereddict partially updated",
mp->ma_used, i);
break;
}
Py_DECREF(it);
Py_RETURN_NONE;
}
if (i >= mp->ma_used) {
PyErr_Format(PyExc_ValueError,
"ordereddict setvalues requires sequence of max length #%zd; "
"a longer sequence was provided, ordereddict fully updated",
mp->ma_used);
Py_DECREF(it);
return NULL;
}
tmp = *epp++;
Py_DECREF(tmp->me_value);
tmp->me_value = item;
}
Py_XDECREF(item);
Py_DECREF(it);
return NULL;
}
static PyObject *
dict_setitems(register PyObject *mp, PyObject *args, PyObject *kwds)
{
PyOrderedDict_Clear((PyObject *)mp);
if (dict_update_common(mp, args, kwds, "|Oi:setitems") != -1)
Py_RETURN_NONE;
return NULL;
}
static PyObject *
dict_rename(register PyOrderedDictObject *mp, PyObject *args)
{
PyObject *oldkey, *newkey;
PyObject *val = NULL;
Py_hash_t hash;
PyOrderedDictEntry *ep, **epp;
register Py_ssize_t index;
if (PySortedDict_CheckExact(mp)) {
PyErr_SetString(PyExc_TypeError,
"sorteddict does not support rename()");
return NULL;
}
if (!PyArg_UnpackTuple(args, "get", 1, 2, &oldkey, &newkey))
return NULL;
if (!PyUNISTR_CheckExact(oldkey) ||
(hash = ((PyUNISTR_Object *) oldkey)->OB_HASH) == -1) {
hash = PyObject_Hash(oldkey);
if (hash == -1)
return NULL;
}
ep = (mp->ma_lookup)(mp, oldkey, hash);
if (ep == NULL || ep->me_value == NULL)
return NULL;
epp = mp->od_otablep;
for (index = 0; index < mp->ma_used; index++, epp++)
if (*epp == ep)
break;
if (*epp != ep)
return NULL; /* this is bad! */
oldkey = ep->me_key; /* now point to key from item */
val = ep->me_value;
Py_INCREF(dummy);
ep->me_key = dummy;
ep->me_value = NULL;
memmove(epp, epp+1, (mp->ma_used - index) * sizeof(PyOrderedDictEntry *));
mp->ma_used--;
Py_DECREF(oldkey);
if(PyOrderedDict_InsertItem(mp, index, newkey, val) != 0)
return NULL;
Py_DECREF(val);
Py_RETURN_NONE;
}
#if PY_VERSION_HEX < 0x03000000
#define REDUCE
/* support for pickling */
static PyObject *
dict_reduce(PyOrderedDictObject *self)
{
PyObject *result, *it, *dict=NULL;
it = dictiter_new(self, &PyOrderedDictIterItem_Type, NULL, NULL);
dict = Py_None;
Py_INCREF(dict);
Py_INCREF(dict);
if (PySortedDict_CheckExact(self)) {
if (((PySortedDictObject *) self)->sd_cmp == NULL)
printf("NULL!!!!\n");
result = Py_BuildValue("O(()OOOi)NNO", self->ob_type,
((PySortedDictObject *) self)->sd_cmp,
((PySortedDictObject *) self)->sd_key,
((PySortedDictObject *) self)->sd_value,
REVERSE(self), dict, dict, it);
} else {
result = Py_BuildValue("O(()ii)NNO", self->ob_type, RELAXED(self), KVIO(self), dict, dict, it);
}
return result;
}
#endif
static PyObject *
ordereddict_getstate(register PyOrderedDictObject *mp)
{
#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(mp->od_state);
#else
return PyInt_FromLong(mp->od_state);
#endif
}
static PyObject *
ordereddict_dump(register PyOrderedDictObject *mp)
{
if (dump_ordereddict_head(mp) != -1)
dump_otablep(mp);
if (PySortedDict_CheckExact(mp))
dump_sorteddict_fun((PySortedDictObject *) mp);
Py_RETURN_NONE;
}
#if PY_VERSION_HEX < 0x03000000
PyDoc_STRVAR(has_key__doc__,
"D.has_key(k) -> True if D has a key k, else False");
#endif
PyDoc_STRVAR(contains__doc__,
"D.__contains__(k) -> True if D has a key k, else False");
#ifdef REDUCE
PyDoc_STRVAR(reduce__doc__, "Return state information for pickling.");
#endif
PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]");
PyDoc_STRVAR(sizeof__doc__,
"D.__sizeof__() -> size of D in memory, in bytes");
PyDoc_STRVAR(get__doc__,
"D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.");
PyDoc_STRVAR(setdefault_doc__,
"D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D");
PyDoc_STRVAR(pop__doc__,
"D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n\
If key is not found, d is returned if given, otherwise KeyError is raised");
PyDoc_STRVAR(popitem__doc__,
"D.popitem([index]) -> (k, v), remove and return indexed (key, value) pair as a\n\
2-tuple (default is last); but raise KeyError if D is empty.");
#if PY_VERSION_HEX < 0x03000000
PyDoc_STRVAR(keys__doc__,
"D.keys([reverse=False]) -> list of D's keys, optionally reversed");
PyDoc_STRVAR(items__doc__,
"D.items() -> list of D's (key, value) pairs, as 2-tuples");
PyDoc_STRVAR(values__doc__,
"D.values() -> list of D's values");
#endif
PyDoc_STRVAR(update__doc__,
"D.update([E,] **F) -> None. Update D from dict/iterable E and F.\n"
"If E present and has a .keys() method, does: for k in E: D[k] = E[k]\n\
If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v\n\
In either case, this is followed by: for k in F: D[k] = F[k]");
PyDoc_STRVAR(fromkeys__doc__,
"dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.\n\
v defaults to None.");
PyDoc_STRVAR(clear__doc__,
"D.clear() -> None. Remove all items from D.");
PyDoc_STRVAR(copy__doc__,
"D.copy() -> a shallow copy of D");
#if PY_VERSION_HEX < 0x03000000
PyDoc_STRVAR(iterkeys__doc__,
"D.iterkeys([reverse=False]) -> an iterator over the keys of D");
PyDoc_STRVAR(itervalues__doc__,
"D.itervalues() -> an iterator over the values of D");
PyDoc_STRVAR(iteritems__doc__,
"D.iteritems() -> an iterator over the (key, value) items of D");
#endif
PyDoc_STRVAR(index_doc,
"D.index(key) -> return position of key in ordered dict");
PyDoc_STRVAR(insert_doc,
"D.insert(index, key, value) -> add/update (key, value) and insert key at index");
PyDoc_STRVAR(reverse_doc,
"D.reverse() -> reverse the order of the keys of D");
PyDoc_STRVAR(setkeys_doc,
"D.setkeys(keys) -> set the keys of D (keys must be iterable and a permutation of .keys())");
PyDoc_STRVAR(setvalues_doc,
"D.setvalues(values) -> set D values to values (must be iterable)");
PyDoc_STRVAR(setitems_doc,
"D.setitems(items) -> clear D and then set items");
PyDoc_STRVAR(rename_doc,
"D.rename(oldkey, newkey) -> exchange keys without changing order");
PyDoc_STRVAR(getstate_doc,
"D.getstate() -> return the state integer");
PyDoc_STRVAR(dump_doc,
"D.dump() -> print internals of an orereddict");
/* Forward */
static PyObject *dictkeys_new(PyObject *);
static PyObject *dictitems_new(PyObject *);
static PyObject *dictvalues_new(PyObject *);
#if PY_VERSION_HEX < 0x03000000
PyDoc_STRVAR(viewkeys__doc__,
"D.viewkeys() -> a set-like object providing a view on D's keys");
PyDoc_STRVAR(viewitems__doc__,
"D.viewitems() -> a set-like object providing a view on D's items");
PyDoc_STRVAR(viewvalues__doc__,
"D.viewvalues() -> an object providing a view on D's values");
#else
PyDoc_STRVAR(viewkeys__doc__,
"D.keys() -> a set-like object providing a view on D's keys");
PyDoc_STRVAR(viewitems__doc__,
"D.items() -> a set-like object providing a view on D's items");
PyDoc_STRVAR(viewvalues__doc__,
"D.values() -> an object providing a view on D's values");
#endif
static PyMethodDef ordereddict_methods[] = {
{
"__contains__",(PyCFunction)dict_contains, METH_O | METH_COEXIST,
contains__doc__
},
{
"__getitem__", (PyCFunction)dict_subscript, METH_O | METH_COEXIST,
getitem__doc__
},
{"__sizeof__", (PyCFunction)dict_sizeof, METH_NOARGS,
sizeof__doc__},
#ifdef REDUCE
{"__reduce__", (PyCFunction)dict_reduce, METH_NOARGS, reduce__doc__},
#endif
#if PY_VERSION_HEX < 0x03000000
{
"has_key", (PyCFunction)dict_has_key, METH_O,
has_key__doc__
},
#endif
{
"get", (PyCFunction)dict_get, METH_VARARGS,
get__doc__
},
{
"setdefault", (PyCFunction)dict_setdefault, METH_VARARGS,
setdefault_doc__
},
{
"pop", (PyCFunction)dict_pop, METH_VARARGS,
pop__doc__
},
{
"popitem", (PyCFunction)dict_popitem, METH_VARARGS,
popitem__doc__
},
#if PY_VERSION_HEX < 0x03000000
{
"keys", (PyCFunction)dict_keys, METH_VARARGS | METH_KEYWORDS,
keys__doc__
},
{
"items", (PyCFunction)dict_items, METH_VARARGS | METH_KEYWORDS,
items__doc__
},
{
"values", (PyCFunction)dict_values, METH_VARARGS | METH_KEYWORDS,
values__doc__
},
#if PY_VERSION_HEX >= 0x02070000
{"viewkeys", (PyCFunction)dictkeys_new, METH_NOARGS,
viewkeys__doc__},
{"viewitems", (PyCFunction)dictitems_new, METH_NOARGS,
viewitems__doc__},
{"viewvalues", (PyCFunction)dictvalues_new, METH_NOARGS,
viewvalues__doc__},
#endif
#else /* Py3K */
{"keys", (PyCFunction)dictkeys_new, METH_NOARGS,
viewkeys__doc__},
{"items", (PyCFunction)dictitems_new, METH_NOARGS,
viewitems__doc__},
{"values", (PyCFunction)dictvalues_new, METH_NOARGS,
viewvalues__doc__},
#endif
{
"update", (PyCFunction)dict_update, METH_VARARGS | METH_KEYWORDS,
update__doc__
},
{
"fromkeys", (PyCFunction)dict_fromkeys, METH_VARARGS | METH_CLASS,
fromkeys__doc__
},
{
"clear", (PyCFunction)dict_clear, METH_NOARGS,
clear__doc__
},
{
"copy", (PyCFunction)dict_copy, METH_NOARGS,
copy__doc__
},
#if PY_VERSION_HEX < 0x03000000
{
"iterkeys", (PyCFunction)dict_iterkeys, METH_VARARGS | METH_KEYWORDS,
iterkeys__doc__
},
{
"itervalues", (PyCFunction)dict_itervalues, METH_VARARGS | METH_KEYWORDS,
itervalues__doc__
},
{
"iteritems", (PyCFunction)dict_iteritems, METH_VARARGS | METH_KEYWORDS,
iteritems__doc__
},
#endif
{"index", (PyCFunction)dict_index, METH_O, index_doc},
{"insert", (PyCFunction)dict_insert, METH_VARARGS, insert_doc},
{"reverse", (PyCFunction)dict_reverse, METH_NOARGS, reverse_doc},
{"setkeys", (PyCFunction)dict_setkeys, METH_O, setkeys_doc},
{"setvalues", (PyCFunction)dict_setvalues, METH_O, setvalues_doc},
{"setitems", (PyCFunction)dict_setitems, METH_VARARGS | METH_KEYWORDS, setitems_doc},
{"rename", (PyCFunction)dict_rename, METH_VARARGS, rename_doc},
{"getstate", (PyCFunction)ordereddict_getstate, METH_NOARGS, getstate_doc},
{"dump", (PyCFunction)ordereddict_dump, METH_NOARGS, dump_doc},
{NULL, NULL} /* sentinel */
};
/* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */
int
PyOrderedDict_Contains(PyObject *op, PyObject *key)
{
Py_hash_t hash;
PyOrderedDictObject *mp = (PyOrderedDictObject *)op;
PyOrderedDictEntry *ep;
if (!PyUNISTR_CheckExact(key) ||
(hash = ((PyUNISTR_Object *) key)->OB_HASH) == -1) {
hash = PyObject_Hash(key);
if (hash == -1)
return -1;
}
ep = (mp->ma_lookup)(mp, key, hash);
return ep == NULL ? -1 : (ep->me_value != NULL);
}
/* Internal version of PyOrderedDict_Contains used when the hash value is already known */
int
_PyOrderedDict_Contains(PyObject *op, PyObject *key, Py_hash_t hash)
{
PyOrderedDictObject *mp = (PyOrderedDictObject *)op;
PyOrderedDictEntry *ep;
ep = (mp->ma_lookup)(mp, key, hash);
return ep == NULL ? -1 : (ep->me_value != NULL);
}
static PyObject *
PyOderedDict_Slice(PyObject *op, register Py_ssize_t ilow,
register Py_ssize_t ihigh)
{
PyOrderedDictObject *mp = (PyOrderedDictObject *)op;
PyOrderedDictObject *slice;
if (mp == NULL || !PyOrderedDict_Check(mp)) {
PyErr_BadInternalCall();
return NULL;
}
slice = (PyOrderedDictObject *) PyOrderedDict_New();
if (slice == NULL)
return NULL;
/* [:] -> ilow = 0, ihigh MAXINT */
if (ilow < 0)
ilow += mp->ma_used;
if (ihigh < 0)
ihigh += mp->ma_used;
if (ilow < 0)
ilow = 0;
else if (ilow > mp->ma_used)
ilow = mp->ma_used;
if (ihigh < ilow)
ihigh = ilow;
else if (ihigh > mp->ma_used)
ihigh = mp->ma_used;
if (PyOrderedDict_CopySome((PyObject *) slice,
op, ilow, 1, (ihigh-ilow), 1) == 0) {
return (PyObject *) slice;
}
Py_DECREF(slice);
return NULL;
}
/* Hack to implement "key in dict" */
static PySequenceMethods dict_as_sequence = {
0, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
(ssizessizeargfunc)PyOderedDict_Slice, /* sq_slice */
0, /* sq_ass_item */
(ssizessizeobjargproc)dict_ass_slice, /* sq_ass_slice */
PyOrderedDict_Contains, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
};
static PyObject *
dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *self;
assert(type != NULL && type->tp_alloc != NULL);
assert(dummy != NULL);
self = type->tp_alloc(type, 0);
if (self != NULL) {
PyOrderedDictObject *d = (PyOrderedDictObject *)self;
/* It's guaranteed that tp->alloc zeroed out the struct. */
assert(d->ma_table == NULL && d->od_fill == 0 && d->ma_used == 0);
INIT_NONZERO_DICT_SLOTS(d);
d->ma_lookup = lookdict_string;
/* The object has been implicitly tracked by tp_alloc */
if (type == &PyOrderedDict_Type)
_PyObject_GC_UNTRACK(d);
#ifdef SHOW_CONVERSION_COUNTS
++created;
#endif
#ifdef SHOW_TRACK_COUNT
if (_PyObject_GC_IS_TRACKED(d))
count_tracked++;
else
count_untracked++;
#endif
}
return self;
}
static PyObject *
sorteddict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *self;
assert(type != NULL && type->tp_alloc != NULL);
assert(dummy != NULL);
self = type->tp_alloc(type, 0);
if (self != NULL) {
PyOrderedDictObject *d = (PyOrderedDictObject *)self;
/* It's guaranteed that tp->alloc zeroed out the struct. */
assert(d->ma_table == NULL && d->od_fill == 0 && d->ma_used == 0);
INIT_NONZERO_DICT_SLOTS(d);
d->ma_lookup = lookdict_string;
INIT_SORT_FUNCS(((PySortedDictObject *) self));
if (type == &PySortedDict_Type)
_PyObject_GC_UNTRACK(d);
#ifdef SHOW_CONVERSION_COUNTS
++created;
#endif
#ifdef SHOW_TRACK_COUNT
if (_PyObject_GC_IS_TRACKED(d))
count_tracked++;
else
count_untracked++;
#endif
}
return self;
}
static int
ordereddict_init(PyObject *self, PyObject *args, PyObject *kwds)
{
PyObject *arg = NULL;
int result = 0, tmprelax = -1, tmpkvio = -1;
static char *kwlist[] = {"src", "relax", "kvio", 0};
if (args != NULL) {
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oii:ordereddict",
kwlist, &arg, &tmprelax, &tmpkvio)) {
return -1;
}
}
if (tmpkvio == -1)
tmpkvio = ordereddict_kvio;
if (tmpkvio)
((PyOrderedDictObject *)self)->od_state |= OD_KVIO_BIT;
if (tmprelax == -1)
tmprelax = ordereddict_relaxed;
if (tmprelax)
((PyOrderedDictObject *)self)->od_state |= OD_RELAXED_BIT;
if (arg != NULL) {
if (PyObject_HasAttrString(arg, "keys"))
result = PyOrderedDict_Merge(self, arg, 1, tmprelax);
else
result = PyOrderedDict_MergeFromSeq2(self, arg, 1);
}
/* do not initialise from keywords at all */
return result;
}
static int
sorteddict_init(PyObject *self, PyObject *args, PyObject *kwds)
{
PyObject *arg = NULL, *cmpfun = NULL, *keyfun = NULL, *valuefun = NULL;
int result = 0, reverse = 0;
static char *kwlist[] = {"src", "cmp", "key", "value", "reverse", 0};
if (args != NULL)
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOOi:sorteddict",
kwlist, &arg, &cmpfun, &keyfun, &valuefun, &reverse))
return -1;
if (reverse)
((PyOrderedDictObject *)self)->od_state |= OD_REVERSE_BIT;
/* always relaxed about order of source */
((PyOrderedDictObject *)self)->od_state |= OD_RELAXED_BIT;
if (keyfun != NULL && keyfun != Py_False)
((PySortedDictObject *)self)->sd_key = keyfun;
if (arg != NULL) {
if (PyObject_HasAttrString(arg, "keys"))
result = PyOrderedDict_Merge(self, arg, 1, 1);
else
result = PyOrderedDict_MergeFromSeq2(self, arg, 1);
}
/* do not initialise from keywords at all */
return result;
}
static PyObject *
dict_iter(PyOrderedDictObject *dict)
{
return dictiter_new(dict, &PyOrderedDictIterKey_Type, NULL, NULL);
}
PyDoc_STRVAR(ordereddict_doc,
"ordereddict() -> new empty dictionary.\n"
"dict(orderddict) -> new dictionary initialized from a mappings object's\n"
" (key, value) pairs.\n"
//"dict(iterable) -> new dictionary initialized as if via:\n"
//" d = {}\n"
//" for k, v in iterable:\n"
//" 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)"
);
PyTypeObject PyOrderedDict_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_ordereddict.ordereddict",
sizeof(PyOrderedDictObject),
0,
(destructor)dict_dealloc, /* tp_dealloc */
#if PY_MAJOR_VERSION < 3
(printfunc)ordereddict_print, /* tp_print */
#else
0, /* tp_print */
#endif
0, /* tp_getattr */
0, /* tp_setattr */
#if PY_MAJOR_VERSION < 3
(cmpfunc)dict_compare, /* tp_compare */
#else
0, /* tp_reserved */
#endif
(reprfunc)ordereddict_repr, /* tp_repr */
0, /* tp_as_number */
&dict_as_sequence, /* tp_as_sequence */
&dict_as_mapping, /* tp_as_mapping */
(hashfunc)PyObject_HashNotImplemented, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DICT_SUBCLASS, /* tp_flags */
ordereddict_doc, /* tp_doc */
dict_traverse, /* tp_traverse */
dict_tp_clear, /* tp_clear */
dict_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)dict_iter, /* tp_iter */
0, /* tp_iternext */
ordereddict_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
DEFERRED_ADDRESS(&PyDict_Type), /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
ordereddict_init, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
dict_new, /* tp_new */
PyObject_GC_Del, /* tp_free */
};
PyDoc_STRVAR(sorteddict_doc,
"sorteddict() -> new empty dictionary.\n"
);
PyTypeObject PySortedDict_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_ordereddict.sorteddict",
sizeof(PySortedDictObject),
0,
(destructor)dict_dealloc, /* tp_dealloc */
#if PY_MAJOR_VERSION < 3
(printfunc)ordereddict_print, /* tp_print */
#else
0, /* tp_print */
#endif
0, /* tp_getattr */
0, /* tp_setattr */
#if PY_MAJOR_VERSION < 3
(cmpfunc)dict_compare, /* tp_compare */
#else
0, /* tp_reserved */
#endif
(reprfunc)sorteddict_repr, /* tp_repr */
0, /* tp_as_number */
&dict_as_sequence, /* tp_as_sequence */
&dict_as_mapping, /* tp_as_mapping */
(hashfunc)PyObject_HashNotImplemented, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DICT_SUBCLASS, /* tp_flags */
sorteddict_doc, /* tp_doc */
dict_traverse, /* tp_traverse */
dict_tp_clear, /* tp_clear */
dict_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)dict_iter, /* tp_iter */
0, /* tp_iternext */
ordereddict_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
DEFERRED_ADDRESS(&PyDict_Type), /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
sorteddict_init, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
sorteddict_new, /* tp_new */
PyObject_GC_Del, /* tp_free */
};
/* Dictionary iterator types */
typedef struct {
PyObject_HEAD
PyOrderedDictObject *di_dict; /* Set to NULL when iterator is exhausted */
Py_ssize_t di_used;
Py_ssize_t di_pos;
PyObject* di_result; /* reusable result tuple for iteritems */
Py_ssize_t len;
int step;
} ordereddictiterobject;
static PyObject *
dictiter_new(PyOrderedDictObject *dict, PyTypeObject *itertype,
PyObject *args, PyObject *kwds)
{
ordereddictiterobject *di;
int reverse = 0;
static char *kwlist[] = {"reverse", 0};
if (args != NULL)
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i:keys",
kwlist, &reverse))
return NULL;
/* review: introduce GC_New */
di = PyObject_GC_New(ordereddictiterobject, itertype);
if (di == NULL)
return NULL;
Py_INCREF(dict);
di->di_dict = dict;
di->di_used = dict->ma_used;
di->len = dict->ma_used;
if (reverse) {
di->di_pos = (dict->ma_used) - 1;
di->step = -1;
} else {
di->di_pos = 0;
di->step = 1;
}
if (itertype == &PyOrderedDictIterItem_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;
PyObject_GC_Track(di);
return (PyObject *)di;
}
static void
dictiter_dealloc(ordereddictiterobject *di)
{
Py_XDECREF(di->di_dict);
Py_XDECREF(di->di_result);
PyObject_GC_Del(di);
}
static int
dictiter_traverse(ordereddictiterobject *di, visitproc visit, void *arg)
{
Py_VISIT(di->di_dict);
Py_VISIT(di->di_result);
return 0;
}
static PyObject *
dictiter_len(ordereddictiterobject *di)
{
Py_ssize_t len = 0;
if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used)
len = di->len;
#if PY_VERSION_HEX < 0x03000000
return PyInt_FromSize_t(len);
#else
return PyLong_FromSize_t(len);
#endif
}
PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
static PyMethodDef dictiter_methods[] = {
{"__length_hint__", (PyCFunction)dictiter_len, METH_NOARGS, length_hint_doc},
{NULL, NULL} /* sentinel */
};
static PyObject *dictiter_iternextkey(ordereddictiterobject *di)
{
PyObject *key;
register Py_ssize_t i;
register PyOrderedDictEntry **epp;
PyOrderedDictObject *d = di->di_dict;
if (d == NULL)
return NULL;
assert (PyOrderedDict_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;
if (i >= d->ma_used)
goto fail;
epp = d->od_otablep;
di->di_pos = i+di->step;
di->len--; /* len can be calculated */
key = epp[i]->me_key;
Py_INCREF(key);
return key;
fail:
Py_DECREF(d);
di->di_dict = NULL;
return NULL;
}
PyTypeObject PyOrderedDictIterKey_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_ordereddict.keyiterator", /* tp_name */
sizeof(ordereddictiterobject), /* 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 */
0, /* tp_as_sequence */
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 | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc)dictiter_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
(iternextfunc)dictiter_iternextkey, /* tp_iternext */
dictiter_methods, /* tp_methods */
0,
};
static PyObject *dictiter_iternextvalue(ordereddictiterobject *di)
{
PyObject *value;
register Py_ssize_t i;
register PyOrderedDictEntry **epp;
PyOrderedDictObject *d = di->di_dict;
if (d == NULL)
return NULL;
assert (PyOrderedDict_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 || i >= d->ma_used)
goto fail;
epp = d->od_otablep;
di->di_pos = i+di->step;
di->len--; /* len can be calculated */
value = epp[i]->me_value;
Py_INCREF(value);
return value;
fail:
Py_DECREF(d);
di->di_dict = NULL;
return NULL;
}
PyTypeObject PyOrderedDictIterValue_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_ordereddict.valueiterator", /* tp_name */
sizeof(ordereddictiterobject), /* 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 */
0, /* tp_as_sequence */
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 | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc)dictiter_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
(iternextfunc)dictiter_iternextvalue, /* tp_iternext */
dictiter_methods, /* tp_methods */
0,
};
static PyObject *dictiter_iternextitem(ordereddictiterobject *di)
{
PyObject *key, *value, *result = di->di_result;
register Py_ssize_t i;
register PyOrderedDictEntry **epp;
PyOrderedDictObject *d = di->di_dict;
if (d == NULL)
return NULL;
assert (PyOrderedDict_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;
/* review: differs in 2.5.6 */
if (i >= d->ma_used)
goto fail;
epp = d->od_otablep;
di->di_pos = i+di->step;
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);
if (result == NULL)
return NULL;
}
di->len--; /* len can be calculated */
key = epp[i]->me_key;
value = epp[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 PyOrderedDictIterItem_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"_ordereddict.itemiterator", /* tp_name */
sizeof(ordereddictiterobject), /* 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 */
0, /* tp_as_sequence */
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 | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
(traverseproc)dictiter_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
(iternextfunc)dictiter_iternextitem, /* tp_iternext */
dictiter_methods, /* tp_methods */
0,
};
/*******************************************************************/
static PyObject *
getset_relaxed(PyObject *self, PyObject *args)
{
int n = -1, oldval = ordereddict_relaxed;
if (!PyArg_ParseTuple(args, "|i", &n))
return NULL;
if (n != -1) {
ordereddict_relaxed = n;
}
return PyBool_FromLong(oldval);
}
static PyObject *
getset_kvio(PyObject *self, PyObject *args)
{
int n = -1, oldval = ordereddict_kvio;
if (!PyArg_ParseTuple(args, "|i", &n))
return NULL;
if (n != -1) {
ordereddict_kvio = n;
}
return PyBool_FromLong(oldval);
}
static PyMethodDef ordereddict_functions[] = {
{
"relax", getset_relaxed, METH_VARARGS,
"get/set routine for allowing global undeordered dict initialisation"
},
{
"kvio", getset_kvio, METH_VARARGS,
"get/set routine for allowing global KeyValue Insertion Order initialisation"
},
{NULL, NULL} /* sentinel */
};
#if PY_VERSION_HEX >= 0x02070000
/* dictionary views are 2.7+ */
/***********************************************/
/* View objects for keys(), items(), values(). */
/***********************************************/
/* The instance lay-out is the same for all three; but the type differs. */
typedef struct {
PyObject_HEAD
PyOrderedDictObject *dv_dict;
} dictviewobject;
static void
dictview_dealloc(dictviewobject *dv)
{
Py_XDECREF(dv->dv_dict);
PyObject_GC_Del(dv);
}
static int
dictview_traverse(dictviewobject *dv, visitproc visit, void *arg)
{
Py_VISIT(dv->dv_dict);
return 0;
}
static Py_ssize_t
dictview_len(dictviewobject *dv)
{
Py_ssize_t len = 0;
if (dv->dv_dict != NULL)
len = dv->dv_dict->ma_used;
return len;
}
static PyObject *
dictview_new(PyObject *dict, PyTypeObject *type)
{
dictviewobject *dv;
if (dict == NULL) {
PyErr_BadInternalCall();
return NULL;
}
if (!PyDict_Check(dict)) {
/* XXX Get rid of this restriction later */
PyErr_Format(PyExc_TypeError,
"%s() requires a dict argument, not '%s'",
type->tp_name, dict->ob_type->tp_name);
return NULL;
}
dv = PyObject_GC_New(dictviewobject, type);
if (dv == NULL)
return NULL;
Py_INCREF(dict);
dv->dv_dict = (PyOrderedDictObject *)dict;
PyObject_GC_Track(dv);
return (PyObject *)dv;
}
/* TODO(guido): The views objects are not complete:
* support more set operations
* support arbitrary mappings?
- either these should be static or exported in dictobject.h
- if public then they should probably be in builtins
*/
/* Return 1 if self is a subset of other, iterating over self;
0 if not; -1 if an error occurred. */
static int
all_contained_in(PyObject *self, PyObject *other)
{
PyObject *iter = PyObject_GetIter(self);
int ok = 1;
if (iter == NULL)
return -1;
for (;;) {
PyObject *next = PyIter_Next(iter);
if (next == NULL) {
if (PyErr_Occurred())
ok = -1;
break;
}
ok = PySequence_Contains(other, next);
Py_DECREF(next);
if (ok <= 0)
break;
}
Py_DECREF(iter);
return ok;
}
static PyObject *
dictview_richcompare(PyObject *self, PyObject *other, int op)
{
Py_ssize_t len_self, len_other;
int ok;
PyObject *result;
assert(self != NULL);
assert(PyDictViewSet_Check(self));
assert(other != NULL);
if (!PyAnySet_Check(other) && !PyDictViewSet_Check(other)) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
len_self = PyObject_Size(self);
if (len_self < 0)
return NULL;
len_other = PyObject_Size(other);
if (len_other < 0)
return NULL;
ok = 0;
switch(op) {
case Py_NE:
case Py_EQ:
if (len_self == len_other)
ok = all_contained_in(self, other);
if (op == Py_NE && ok >= 0)
ok = !ok;
break;
case Py_LT:
if (len_self < len_other)
ok = all_contained_in(self, other);
break;
case Py_LE:
if (len_self <= len_other)
ok = all_contained_in(self, other);
break;
case Py_GT:
if (len_self > len_other)
ok = all_contained_in(other, self);
break;
case Py_GE:
if (len_self >= len_other)
ok = all_contained_in(other, self);
break;
}
if (ok < 0)
return NULL;
result = ok ? Py_True : Py_False;
Py_INCREF(result);
return result;
}
static PyObject *
dictview_repr(dictviewobject *dv)
{
PyObject *seq;
PyObject *result;
#if PY_MAJOR_VERSION < 3
PyObject *seq_str;
#endif
seq = PySequence_List((PyObject *)dv);
if (seq == NULL)
return NULL;
#if PY_MAJOR_VERSION < 3
seq_str = PyObject_Repr(seq);
if (seq_str == NULL) {
Py_DECREF(seq);
return NULL;
}
result = PyUNISTR_FromFormat("%s(%s)", Py_TYPE(dv)->tp_name,
PyString_AS_STRING(seq_str));
Py_DECREF(seq_str);
#else
result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq);
#endif
Py_DECREF(seq);
return result;
}
/*** dict_keys ***/
static PyObject *
dictkeys_iter(dictviewobject *dv)
{
if (dv->dv_dict == NULL) {
Py_RETURN_NONE;
}
return dictiter_new(dv->dv_dict, &PyOrderedDictIterKey_Type, NULL, NULL);
}
static int
dictkeys_contains(dictviewobject *dv, PyObject *obj)
{
if (dv->dv_dict == NULL)
return 0;
return PyDict_Contains((PyObject *)dv->dv_dict, obj);
}
static PySequenceMethods dictkeys_as_sequence = {
(lenfunc)dictview_len, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
(objobjproc)dictkeys_contains, /* sq_contains */
};
static PyObject*
dictviews_sub(PyObject* self, PyObject *other)
{
PyObject *result = PySet_New(self);
PyObject *tmp;
if (result == NULL)
return NULL;
tmp = PyObject_CallMethod(result, "difference_update", "O", other);
if (tmp == NULL) {
Py_DECREF(result);
return NULL;
}
Py_DECREF(tmp);
return result;
}
static PyObject*
dictviews_and(PyObject* self, PyObject *other)
{
PyObject *result = PySet_New(self);
PyObject *tmp;
if (result == NULL)
return NULL;
tmp = PyObject_CallMethod(result, "intersection_update", "O", other);
if (tmp == NULL) {
Py_DECREF(result);
return NULL;
}
Py_DECREF(tmp);
return result;
}
static PyObject*
dictviews_or(PyObject* self, PyObject *other)
{
PyObject *result = PySet_New(self);
PyObject *tmp;
if (result == NULL)
return NULL;
tmp = PyObject_CallMethod(result, "update", "O", other);
if (tmp == NULL) {
Py_DECREF(result);
return NULL;
}
Py_DECREF(tmp);
return result;
}
static PyObject*
dictviews_xor(PyObject* self, PyObject *other)
{
PyObject *result = PySet_New(self);
PyObject *tmp;
if (result == NULL)
return NULL;
tmp = PyObject_CallMethod(result, "symmetric_difference_update", "O",
other);
if (tmp == NULL) {
Py_DECREF(result);
return NULL;
}
Py_DECREF(tmp);
return result;
}
static PyNumberMethods dictviews_as_number = {
0, /*nb_add*/
(binaryfunc)dictviews_sub, /*nb_subtract*/
0, /*nb_multiply*/
#if PY_MAJOR_VERSION < 3
0, /*nb_divide*/
#endif
0, /*nb_remainder*/
0, /*nb_divmod*/
0, /*nb_power*/
0, /*nb_negative*/
0, /*nb_positive*/
0, /*nb_absolute*/
0, /*nb_nonzero/nb_bool*/
0, /*nb_invert*/
0, /*nb_lshift*/
0, /*nb_rshift*/
(binaryfunc)dictviews_and, /*nb_and*/
(binaryfunc)dictviews_xor, /*nb_xor*/
(binaryfunc)dictviews_or, /*nb_or*/
};
static PyMethodDef dictkeys_methods[] = {
{NULL, NULL} /* sentinel */
};
PyTypeObject PyOrderedDictKeys_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"dict_keys", /* tp_name */
sizeof(dictviewobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)dictview_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)dictview_repr, /* tp_repr */
&dictviews_as_number, /* tp_as_number */
&dictkeys_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
#if PY_MAJOR_VERSION < 3
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_CHECKTYPES, /* tp_flags */
#else
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
#endif
0, /* tp_doc */
(traverseproc)dictview_traverse, /* tp_traverse */
0, /* tp_clear */
dictview_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)dictkeys_iter, /* tp_iter */
0, /* tp_iternext */
dictkeys_methods, /* tp_methods */
0,
};
static PyObject *
dictkeys_new(PyObject *dict)
{
return dictview_new(dict, &PyOrderedDictKeys_Type);
}
/*** dict_items ***/
static PyObject *
dictitems_iter(dictviewobject *dv)
{
if (dv->dv_dict == NULL) {
Py_RETURN_NONE;
}
return dictiter_new(dv->dv_dict, &PyOrderedDictIterItem_Type, NULL, NULL);
}
static int
dictitems_contains(dictviewobject *dv, PyObject *obj)
{
PyObject *key, *value, *found;
if (dv->dv_dict == NULL)
return 0;
if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2)
return 0;
key = PyTuple_GET_ITEM(obj, 0);
value = PyTuple_GET_ITEM(obj, 1);
found = PyDict_GetItem((PyObject *)dv->dv_dict, key);
if (found == NULL) {
if (PyErr_Occurred())
return -1;
return 0;
}
return PyObject_RichCompareBool(value, found, Py_EQ);
}
static PySequenceMethods dictitems_as_sequence = {
(lenfunc)dictview_len, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
(objobjproc)dictitems_contains, /* sq_contains */
};
static PyMethodDef dictitems_methods[] = {
{NULL, NULL} /* sentinel */
};
PyTypeObject PyOrderedDictItems_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"dict_items", /* tp_name */
sizeof(dictviewobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)dictview_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)dictview_repr, /* tp_repr */
&dictviews_as_number, /* tp_as_number */
&dictitems_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
#if PY_MAJOR_VERSION < 3
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_CHECKTYPES, /* tp_flags */
#else
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
#endif
0, /* tp_doc */
(traverseproc)dictview_traverse, /* tp_traverse */
0, /* tp_clear */
dictview_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)dictitems_iter, /* tp_iter */
0, /* tp_iternext */
dictitems_methods, /* tp_methods */
0,
};
static PyObject *
dictitems_new(PyObject *dict)
{
return dictview_new(dict, &PyOrderedDictItems_Type);
}
/*** dict_values ***/
static PyObject *
dictvalues_iter(dictviewobject *dv)
{
if (dv->dv_dict == NULL) {
Py_RETURN_NONE;
}
return dictiter_new(dv->dv_dict, &PyOrderedDictIterValue_Type, NULL, NULL);
}
static PySequenceMethods dictvalues_as_sequence = {
(lenfunc)dictview_len, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
(objobjproc)0, /* sq_contains */
};
static PyMethodDef dictvalues_methods[] = {
{NULL, NULL} /* sentinel */
};
PyTypeObject PyOrderedDictValues_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"dict_values", /* tp_name */
sizeof(dictviewobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)dictview_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
(reprfunc)dictview_repr, /* tp_repr */
0, /* tp_as_number */
&dictvalues_as_sequence, /* tp_as_sequence */
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 | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
(traverseproc)dictview_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)dictvalues_iter, /* tp_iter */
0, /* tp_iternext */
dictvalues_methods, /* tp_methods */
0,
};
static PyObject *
dictvalues_new(PyObject *dict)
{
return dictview_new(dict, &PyOrderedDictValues_Type);
}
#endif /* PY_VERSION_HEX >= 0x02070000 */
/************************************************************************/
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"_ordereddict", /* m_name */
ordereddict_doc, /* m_doc */
-1, /* m_size */
ordereddict_functions, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
#endif
static PyObject *
ruamel_ordereddict_moduleinit(void)
{
PyObject *m;
/* moved here as we have two primitives and dictobject.c had
no initialisation function */
if (dummy == NULL) { /* Auto-initialize dummy */
dummy = PyUNISTR_FromString("<dummy key>");
if (dummy == NULL)
return NULL;
#ifdef SHOW_CONVERSION_COUNTS
Py_AtExit(show_counts);
#endif
}
/* Fill in deferred data addresses. This must be done before
PyType_Ready() is called. Note that PyType_Ready() automatically
initializes the ob.ob_type field to &PyType_Type if it's NULL,
so it's not necessary to fill in ob_type first. */
PyOrderedDict_Type.tp_base = &PyDict_Type;
PySortedDict_Type.tp_base = &PyOrderedDict_Type;
if (PyType_Ready(&PyOrderedDict_Type) < 0)
return NULL;
if (PyType_Ready(&PySortedDict_Type) < 0)
return NULL;
/* AvdN: TODO understand why it is necessary or not (as it seems)
to PyTypeReady the iterator types
*/
#if PY_MAJOR_VERSION >= 3
m = PyModule_Create(&moduledef);
#else
m = Py_InitModule3("_ordereddict",
ordereddict_functions,
ordereddict_doc
// , NULL, PYTHON_API_VERSION
);
#endif
if (m == NULL)
return NULL;
/* this allows PyVarObject_HEAD_INIT to take NULL as first
parameter: https://docs.python.org/3.1/extending/windows.html
*/
if (PyType_Ready(&PyOrderedDict_Type) < 0)
return NULL;
Py_INCREF(&PyOrderedDict_Type);
if (PyModule_AddObject(m, "ordereddict",
(PyObject *) &PyOrderedDict_Type) < 0)
Py_INCREF(&PySortedDict_Type);
if (PyModule_AddObject(m, "sorteddict",
(PyObject *) &PySortedDict_Type) < 0)
return NULL;
return m;
}
#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC init_ordereddict(void)
{
ruamel_ordereddict_moduleinit();
}
#else
PyMODINIT_FUNC PyInit__ordereddict(void)
{
return ruamel_ordereddict_moduleinit();
}
#endif