/*
* Copyright (C) 2000, 2001, 2013 Gregory Trubetskoy
* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* Originally developed by Gregory Trubetskoy.
*
*
* tableobject.c
*
*
*/
#include "mod_python.h"
/** XXX this is a hack. because apr_table_t
is not available in a header file */
#define TABLE_HASH_SIZE 32
struct apr_table_t {
apr_array_header_t a;
#ifdef MAKE_TABLE_PROFILE
void *creator;
#endif
apr_uint32_t index_initialized;
int index_first[TABLE_HASH_SIZE];
int index_last[TABLE_HASH_SIZE];
};
/**
** MpTable_FromTable
**
* This routine creates a Python tableobject given an Apache
* table pointer.
*
*/
PyObject * MpTable_FromTable(apr_table_t *t)
{
tableobject *result;
TABLE_DEBUG("MpTable_FromTable");
result = PyObject_New(tableobject, &MpTable_Type);
if (! result)
return PyErr_NoMemory();
result->table = t;
result->pool = NULL;
return (PyObject *)result;
}
/**
** MpTable_New
**
* This returns a new object of built-in type table.
*
* NOTE: The ap_table gets greated in its own pool, which lives
* throught the live of the tableobject. This is because this
* object may persist from hit to hit.
*
* ALSO NOTE: table_new() also creates tables, independent of this
* (it gets called when table is being subclassed)
*
*/
PyObject * MpTable_New()
{
tableobject *t;
apr_pool_t *p;
TABLE_DEBUG("MpTable_New");
/* XXX need second arg abort function to report mem error */
apr_pool_create_ex(&p, NULL, NULL, NULL);
/* two is a wild guess */
t = (tableobject *)MpTable_FromTable(apr_table_make(p, 2));
/* remember the pointer to our own pool */
t->pool = p;
return (PyObject *)t;
}
/**
** table_dealloc
**
* Frees table's memory
*/
static void table_dealloc(register void *o)
{
tableobject *self = (tableobject *)o;
TABLE_DEBUG("table_dealloc");
if (MpTable_Check(self)) {
if (self->pool)
apr_pool_destroy(self->pool);
PyObject_Del(self);
}
else
Py_TYPE(self)->tp_free((PyObject *)self);
}
/**
** table_print
**
* prints table like a dictionary
* (Useful when debugging)
*/
static int table_print(register tableobject *self, register FILE *fp, register int flags)
{
const apr_array_header_t *ah = NULL;
apr_table_entry_t *elts;
register int i;
TABLE_DEBUG("table_print");
fprintf(fp, "{");
ah = apr_table_elts(self->table);
elts = (apr_table_entry_t *) ah->elts;
i = ah->nelts;
if (i == 0) {
fprintf(fp, "}");
return 0;
}
while (i--)
if (elts[i].key)
{
fprintf(fp, "'%s': '%s'", elts[i].key, elts[i].val);
if (i > 0)
fprintf(fp, ", ");
else
fprintf(fp, "}");
}
return 0;
}
/**
** table_repr
**
* repr table like a dictionary
*/
static PyObject * table_repr(tableobject *self)
{
PyObject *s;
PyObject *t = NULL;
const apr_array_header_t *ah;
apr_table_entry_t *elts;
int i;
TABLE_DEBUG("table_repr");
s = PyBytes_FromString("{");
ah = apr_table_elts (self->table);
elts = (apr_table_entry_t *) ah->elts;
i = ah->nelts;
if (i == 0)
PyBytes_ConcatAndDel(&s, PyBytes_FromString("}"));
while (i--)
if (elts[i].key)
{
t = PyBytes_FromString(elts[i].key);
PyBytes_ConcatAndDel(&s, MpObject_ReprAsBytes(t));;
Py_XDECREF(t);
PyBytes_ConcatAndDel(&s, PyBytes_FromString(": "));
if (elts[i].val) {
t = PyBytes_FromString(elts[i].val);
} else {
t = Py_None;
Py_INCREF(t);
}
PyBytes_ConcatAndDel(&s, MpObject_ReprAsBytes(t));
Py_XDECREF(t);
if (i > 0)
PyBytes_ConcatAndDel(&s, PyBytes_FromString(", "));
else
PyBytes_ConcatAndDel(&s, PyBytes_FromString("}"));
}
#if PY_MAJOR_VERSION < 3
return s;
#else
{
PyObject *str = PyUnicode_FromString(PyBytes_AS_STRING(s));
Py_DECREF(s);
return str;
}
#endif
}
/**
** tablelength
**
* Number of elements in a table. Called
* when you do len(table) in Python.
*/
static Py_ssize_t tablelength(PyObject *self)
{
TABLE_DEBUG("tablelength");
return apr_table_elts(((tableobject *)self)->table)->nelts;
}
/**
** table_subscript
**
* Gets a dictionary item
*/
static PyObject * table_subscript(PyObject *self, register PyObject *key)
{
char *k;
const apr_array_header_t *ah;
apr_table_entry_t *elts;
register int i;
PyObject *list;
TABLE_DEBUG("table_subscript");
MP_ANYSTR_AS_STR(k, key, 1);
if (!k) {
Py_DECREF(key);
return NULL;
}
/* it's possible that we have duplicate keys, so
we can't simply use apr_table_get since that just
returns the first match.
*/
list = PyList_New(0);
if (!list)
return NULL;
ah = apr_table_elts (((tableobject *)self)->table);
elts = (apr_table_entry_t *) ah->elts;
i = ah->nelts;
while (i--)
if (elts[i].key) {
if (apr_strnatcasecmp(elts[i].key, k) == 0) {
PyObject *v = NULL;
if (elts[i].val != NULL)
v = MpBytesOrUnicode_FromString(elts[i].val);
else {
v = Py_None;
Py_INCREF(v);
}
PyList_Insert(list, 0, v);
Py_DECREF(v);
}
}
Py_DECREF(key); /* becasue of MP_ANYSTR_AS_STR */
/* if no match */
if (PyList_Size(list) == 0) {
Py_DECREF(list);
PyErr_SetObject(PyExc_KeyError, key);
return NULL;
}
/* if we got one match */
if (PyList_Size(list) == 1) {
PyObject *v = PyList_GetItem(list, 0);
Py_INCREF(v);
Py_DECREF(list);
return v;
}
/* else we return a list */
return list;
}
/**
** table_ass_subscript
**
* insert into table dictionary-style
* *** NOTE ***
* Since the underlying ap_table_set makes a *copy* of the string,
* there is no need to increment the reference to the Python
* string passed in.
*/
static int table_ass_subscript(PyObject *self, PyObject *key,
PyObject *val)
{
char *k, *v;
TABLE_DEBUG("table_ass_subscript");
MP_ANYSTR_AS_STR(k, key, 1);
if (!k) {
Py_XDECREF(key); /* MP_ANYSTR_AS_STR */
return -1;
}
if (val == NULL)
apr_table_unset(((tableobject *)self)->table, k);
else {
MP_ANYSTR_AS_STR(v, val, 1);
if (!v) {
Py_XDECREF(key); /* MP_ANYSTR_AS_STR */
Py_XDECREF(val); /* MP_ANYSTR_AS_STR */
return -1;
}
apr_table_set(((tableobject *)self)->table, k, v);
}
Py_XDECREF(key); /* MP_ANYSTR_AS_STR */
Py_XDECREF(val); /* MP_ANYSTR_AS_STR */
return 0;
}
/* table as mapping */
static PyMappingMethods table_as_mapping = {
tablelength, /*mp_length*/
table_subscript, /*mp_subscript*/
table_ass_subscript, /*mp_ass_subscript*/
};
/**
** table_keys
**
*
* Implements dictionary's keys() method.
*/
static PyObject * table_keys(register tableobject *self)
{
PyObject *v;
const apr_array_header_t *ah;
apr_table_entry_t *elts;
int i, j;
TABLE_DEBUG("table_keys");
ah = apr_table_elts(self->table);
elts = (apr_table_entry_t *) ah->elts;
v = PyList_New(ah->nelts);
for (i = 0, j = 0; i < ah->nelts; i++)
{
if (elts[i].key)
{
PyObject *key = MpBytesOrUnicode_FromString(elts[i].key);
PyList_SetItem(v, j, key);
j++;
}
}
return v;
}
/**
** table_values
**
*
* Implements dictionary's values() method.
*/
static PyObject * table_values(register tableobject *self)
{
PyObject *v;
const apr_array_header_t *ah;
apr_table_entry_t *elts;
int i, j;
TABLE_DEBUG("table_values");
ah = apr_table_elts(self->table);
elts = (apr_table_entry_t *) ah->elts;
v = PyList_New(ah->nelts);
for (i = 0, j = 0; i < ah->nelts; i++)
{
if (elts[i].key)
{
PyObject *val = NULL;
if (elts[i].val != NULL)
val = MpBytesOrUnicode_FromString(elts[i].val);
else {
val = Py_None;
Py_INCREF(val);
}
PyList_SetItem(v, j, val);
j++;
}
}
return v;
}
/**
** table_items
**
*
* Implements dictionary's items() method.
*/
static PyObject * table_items(register tableobject *self)
{
PyObject *v;
const apr_array_header_t *ah;
apr_table_entry_t *elts;
int i, j;
TABLE_DEBUG("table_items");
ah = apr_table_elts(self->table);
elts = (apr_table_entry_t *) ah->elts;
v = PyList_New(ah->nelts);
for (i = 0, j = 0; i < ah->nelts; i++)
{
if (elts[i].key)
{
PyObject *keyval = Py_BuildValue("(s,s)", elts[i].key, elts[i].val);
PyList_SetItem(v, j, keyval);
j++;
}
}
return v;
}
/**
** table_merge
**
* Since tables can only store strings, key/vals from
* mapping object b will be str()ingized.
*/
static int table_merge(tableobject *a, PyObject *b, int override)
{
/* Do it the generic, slower way */
PyObject *keys = PyMapping_Keys(b);
PyObject *iter;
PyObject *key, *value, *skey, *svalue;
int status;
TABLE_DEBUG("table_merge");
if (keys == NULL) {
TABLE_DEBUG(" table_merge: keys NULL");
return -1;
}
iter = PyObject_GetIter(keys);
Py_DECREF(keys);
if (iter == NULL) {
TABLE_DEBUG(" table_merge: iter NULL");
return -1;
}
for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) {
char *c_skey;
skey = PyObject_Str(key);
if (skey == NULL) {
Py_DECREF(iter);
Py_DECREF(key);
TABLE_DEBUG(" table_merge: skey NULL");
return -1;
}
MP_ANYSTR_AS_STR(c_skey, skey, 0);
if (!c_skey) {
Py_DECREF(key);
Py_DECREF(skey);
TABLE_DEBUG(" table_merge: c_skey NULL");
return -1;
}
if (!override && apr_table_get(a->table, c_skey) != NULL) {
Py_DECREF(key);
Py_DECREF(skey);
continue;
}
value = PyObject_GetItem(b, key);
if (value == NULL) {
Py_DECREF(iter);
Py_DECREF(key);
Py_DECREF(skey);
TABLE_DEBUG(" table_merge: value NULL");
return -1;
}
svalue = PyObject_Str(value);
if (svalue == NULL) {
Py_DECREF(iter);
Py_DECREF(key);
Py_DECREF(skey);
Py_DECREF(value);
TABLE_DEBUG(" table_merge: svalue NULL");
return -1;
}
status = table_ass_subscript((PyObject *)a, skey, svalue);
Py_DECREF(key);
Py_DECREF(value);
Py_DECREF(skey);
Py_DECREF(svalue);
if (status < 0) {
Py_DECREF(iter);
TABLE_DEBUG(" table_merge: status < 0");
return -1;
}
}
Py_DECREF(iter);
if (PyErr_Occurred()) {
/* Iterator completed, via error */
TABLE_DEBUG(" table_merge: PyErr_Occurred()");
return -1;
}
return 0;
}
/**
** table_update
**
*/
static PyObject *table_update(tableobject *self, PyObject *other)
{
TABLE_DEBUG("table_update");
if (table_merge(self, other, 1) < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
/**
** table_mergefromseq2
**
* Similar to PyDict_MergeFromSeq2 (code borrowed from there).
*/
static int table_mergefromseq2(tableobject *self, PyObject *seq2, int override)
{
PyObject *it; /* iter(seq2) */
int i; /* index into seq2 of current element */
PyObject *item; /* seq2[i] */
PyObject *fast; /* item as a 2-tuple or 2-list */
TABLE_DEBUG("table_mergefromseq2");
it = PyObject_GetIter(seq2);
if (it == NULL)
return -1;
for (i = 0; ; ++i) {
PyObject *key, *value, *skey, *svalue;
char *c_skey;
int 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 table update "
"sequence element #%d to a sequence",
i);
goto Fail;
}
n = PySequence_Fast_GET_SIZE(fast);
if (n != 2) {
PyErr_Format(PyExc_ValueError,
"table update sequence element #%d "
"has length %d; 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);
skey = PyObject_Str(key);
if (skey == NULL)
goto Fail;
svalue = PyObject_Str(value);
if (svalue == NULL) {
Py_DECREF(svalue);
goto Fail;
}
MP_ANYSTR_AS_STR(c_skey, skey, 0);
if (!c_skey) {
Py_DECREF(skey);
Py_DECREF(svalue);
goto Fail;
}
if (override || apr_table_get(self->table, c_skey) == NULL) {
int status = table_ass_subscript((PyObject *)self, skey, svalue);
if (status < 0) {
Py_DECREF(skey);
Py_DECREF(svalue);
goto Fail;
}
}
Py_DECREF(skey);
Py_DECREF(svalue);
Py_DECREF(fast);
Py_DECREF(item);
}
i = 0;
goto Return;
Fail:
Py_XDECREF(item);
Py_XDECREF(fast);
i = -1;
Return:
Py_DECREF(it);
return i;
}
/**
** table_copy
**
*/
static PyObject *table_copy(register tableobject *from)
{
tableobject *to = (tableobject *)MpTable_New();
TABLE_DEBUG("table_copy");
if (to != NULL)
apr_table_overlap(to->table, from->table, 0);
return (PyObject*)to;
}
#if PY_MAJOR_VERSION < 3
/**
** table_compare
**
*/
static int table_compare(tableobject *a, tableobject *b)
{
/*
we're so lazy that we just copy tables to dicts
and rely on dict's compare ability. this is not
the best way to do this to say the least
*/
PyObject *ad, *bd;
int result;
TABLE_DEBUG("table_compare");
ad = PyDict_New();
bd = PyDict_New();
PyDict_Merge(ad, (PyObject*)a, 0);
PyDict_Merge(bd, (PyObject*)b, 0);
result = PyObject_Compare(ad, bd);
Py_DECREF(ad);
Py_DECREF(bd);
return result;
}
#endif
/**
** table_richcompare
**
*/
static PyObject *table_richcompare(PyObject *a, PyObject *b, int op)
{
PyObject *ad, *bd, *result;
TABLE_DEBUG("table_richcompare");
ad = PyDict_New();
bd = PyDict_New();
PyDict_Merge(ad, (PyObject*)a, 0);
PyDict_Merge(bd, (PyObject*)b, 0);
result = PyObject_RichCompare(ad, bd, op);
Py_DECREF(ad);
Py_DECREF(bd);
return result;
}
/**
** table_has_key
**
*/
static PyObject * table_has_key(tableobject *self, PyObject *key)
{
const char *k;
TABLE_DEBUG("table_has_key");
MP_ANYSTR_AS_STR(k, key, 1);
if (!k) {
Py_DECREF(key); /* MP_ANYSTR_AS_STR */
return NULL;
}
if (apr_table_get(self->table, k))
return PyLong_FromLong(1);
else
return PyLong_FromLong(0);
}
/**
** table_get
**
* implements get([failobj]) method
* (only returns the first match)
*/
static PyObject *table_get(register tableobject *self, PyObject *args)
{
PyObject *key;
PyObject *failobj = Py_None;
PyObject *val = NULL;
const char *k, *v;
TABLE_DEBUG("table_get");
if (!PyArg_ParseTuple(args, "O|O:get", &key, &failobj))
return NULL;
MP_ANYSTR_AS_STR(k, key, 1);
if (!k) {
Py_DECREF(key); /* MP_ANYSTR_AS_STR */
return NULL;
}
v = apr_table_get(self->table, k);
if (!v) {
val = failobj;
Py_INCREF(val);
}
else
val = MpBytesOrUnicode_FromString(v);
Py_DECREF(key); /* MP_ANYSTR_AS_STR */
return val;
}
/**
** table_setdefault
**
* implements setdefault(key, [val]) method
*/
static PyObject *table_setdefault(register tableobject *self, PyObject *args)
{
int len;
PyObject *failobj = NULL;
PyObject *key, *val = NULL;
char *k = NULL, *f = NULL;
const char *v = NULL;
TABLE_DEBUG("table_setdefault");
if (!PyArg_ParseTuple(args, "O|O:setdefault", &key, &failobj))
return NULL;
MP_ANYSTR_AS_STR(k, key, 1);
if (!k) {
Py_DECREF(key); /* MP_ANYSTR_AS_STR */
return NULL;
}
if (failobj) {
MP_ANYSTR_AS_STR(f, failobj, 1);
if (!f) {
Py_DECREF(failobj); /* MP_ANYSTR_AS_ATR */
return NULL;
}
}
v = apr_table_get(self->table, k);
if (!v) {
if (f) {
apr_table_set(self->table, k, f);
val = failobj;
Py_INCREF(val);
}
else {
apr_table_set(self->table, k, "");
v = "";
}
}
val = MpBytesOrUnicode_FromString(v);
Py_XDECREF(failobj); /* MP_ANYSTR_AS_ATR */
return val;
}
/**
** table_clear
**
*/
static PyObject *table_clear(register tableobject *self)
{
TABLE_DEBUG("table_clear");
apr_table_clear(self->table);
Py_INCREF(Py_None);
return Py_None;
}
/**
** table_popitem
**
*/
static PyObject *table_popitem(tableobject *self)
{
apr_array_header_t *ah;
apr_table_entry_t *elts;
PyObject *res;
TABLE_DEBUG("table_popitem");
ah = (apr_array_header_t *) apr_table_elts(self->table);
elts = (apr_table_entry_t *) ah->elts;
if (ah->nelts == 0) {
PyErr_SetString(PyExc_KeyError,
"popitem(): table is empty");
return NULL;
}
res = Py_BuildValue("(s,s)", elts[0].key, elts[0].val);
ah->nelts--;
elts++;
return res;
}
/**
** table_traverse
**
*/
static int table_traverse(tableobject *self, visitproc visit, void *arg)
{
const apr_array_header_t *ah;
apr_table_entry_t *elts;
register int i;
TABLE_DEBUG("table_traverse");
ah = apr_table_elts (self->table);
elts = (apr_table_entry_t *) ah->elts;
i = ah->nelts;
while (i--)
if (elts[i].key) {
int err;
PyObject *v = NULL;
if (elts[i].val != NULL)
v = MpBytesOrUnicode_FromString(elts[i].val);
else {
v = Py_None;
Py_INCREF(v);
}
err = visit(v, arg);
Py_XDECREF(v);
if (err)
return err;
}
return 0;
}
/**
** table_tp_clear
**
*/
static int table_tp_clear(tableobject *self)
{
TABLE_DEBUG("table_tp_clear");
table_clear(self);
return 0;
}
/**
** mp_table_add
**
* this function is equivalent of ap_table_add -
* it can create duplicate entries.
*/
static PyObject * mp_table_add(tableobject *self, PyObject *args)
{
PyObject *key, *val;
const char *k, *v;
TABLE_DEBUG("mp_table_add");
if (! PyArg_ParseTuple(args, "OO", &key, &val))
return NULL;
MP_ANYSTR_AS_STR(k, key, 1);
MP_ANYSTR_AS_STR(v, val, 1);
if ((!k) || (!v)) {
Py_DECREF(key); /* MP_ANYSTR_AS_STR */
Py_DECREF(val); /* MP_ANYSTR_AS_STR */
return NULL;
}
apr_table_add(self->table, k, v);
Py_DECREF(key); /* MP_ANYSTR_AS_STR */
Py_DECREF(val); /* MP_ANYSTR_AS_STR */
Py_INCREF(Py_None);
return Py_None;
}
typedef PyObject * (*tableselectfunc)(apr_table_entry_t *);
static PyObject *tableiter_new(tableobject *, tableselectfunc);
static PyObject *select_key(apr_table_entry_t *elts)
{
return MpBytesOrUnicode_FromString(elts->key);
}
static PyObject *select_value(apr_table_entry_t *elts)
{
PyObject *val = NULL;
TABLE_DEBUG("select_value");
if (elts->val != NULL)
val = MpBytesOrUnicode_FromString(elts->val);
else {
val = Py_None;
Py_INCREF(val);
}
return val;
}
static PyObject *select_item(apr_table_entry_t *elts)
{
TABLE_DEBUG("select_item");
return Py_BuildValue("(s,s)", elts->key, elts->val);
}
static PyObject *table_iterkeys(tableobject *self)
{
TABLE_DEBUG("table_iterkeys");
return tableiter_new(self, select_key);
}
static PyObject *table_itervalues(tableobject *self)
{
TABLE_DEBUG("table_itervalues");
return tableiter_new(self, select_value);
}
static PyObject *table_iteritems(tableobject *self)
{
TABLE_DEBUG("table_iteritems");
return tableiter_new(self, select_item);
}
static char has_key__doc__[] =
"T.has_key(k) -> 1 if T has a key k, else 0";
static char get__doc__[] =
"T.get(k[,d]) -> T[k] if T.has_key(k), else d. d defaults to None.";
static char setdefault_doc__[] =
"T.setdefault(k[,d]) -> T.get(k,d), also set T[k]=d if not T.has_key(k)";
static char popitem__doc__[] =
"T.popitem() -> (k, v), remove and return some (key, value) pair as a\n\
2-tuple; but raise KeyError if T is empty";
static char keys__doc__[] =
"T.keys() -> list of T's keys";
static char items__doc__[] =
"T.items() -> list of T's (key, value) pairs, as 2-tuples";
static char values__doc__[] =
"T.values() -> list of T's values";
static char update__doc__[] =
"T.update(E) -> None. Update T from E: for k in E.keys(): T[k] = E[k]";
static char clear__doc__[] =
"T.clear() -> None. Remove all items from T.";
static char copy__doc__[] =
"T.copy() -> a shallow copy of T";
static char iterkeys__doc__[] =
"T.iterkeys() -> an iterator over the keys of T";
static char itervalues__doc__[] =
"T.itervalues() -> an iterator over the values of T";
static char iteritems__doc__[] =
"T.iteritems() -> an iterator over the (key, value) items of T";
static char add__doc__[] =
"T.add(k, v) -> add (as oppsed to replace) a key k and value v";
/* table method definitions */
static PyMethodDef mp_table_methods[] = {
{"has_key", (PyCFunction)table_has_key, METH_O, has_key__doc__},
{"get", (PyCFunction)table_get, METH_VARARGS, get__doc__},
{"setdefault", (PyCFunction)table_setdefault, METH_VARARGS, setdefault_doc__},
{"popitem", (PyCFunction)table_popitem, METH_NOARGS, popitem__doc__},
{"keys", (PyCFunction)table_keys, METH_NOARGS, keys__doc__},
{"items", (PyCFunction)table_items, METH_NOARGS, items__doc__},
{"values", (PyCFunction)table_values, METH_NOARGS, values__doc__},
{"update", (PyCFunction)table_update, METH_O, update__doc__},
{"clear", (PyCFunction)table_clear, METH_NOARGS, clear__doc__},
{"copy", (PyCFunction)table_copy, METH_NOARGS, copy__doc__},
{"iterkeys", (PyCFunction)table_iterkeys, METH_NOARGS, iterkeys__doc__},
{"itervalues", (PyCFunction)table_itervalues, METH_NOARGS, itervalues__doc__},
{"iteritems", (PyCFunction)table_iteritems, METH_NOARGS, iteritems__doc__},
{"add", (PyCFunction)mp_table_add, METH_VARARGS, add__doc__},
{NULL, NULL} /* sentinel */
};
static int table_contains(tableobject *self, PyObject *key)
{
char *k;
const char *v;
int rc;
TABLE_DEBUG("table_contains");
MP_ANYSTR_AS_STR(k, key, 1);
if (!k) {
Py_DECREF(key);
return -1;
}
v = apr_table_get(self->table, k);
Py_DECREF(key);
return (v != NULL);
}
static PySequenceMethods table_as_sequence = {
0, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
(objobjproc)table_contains, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
};
/**
** table_new
**
*/
static PyObject *table_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
TABLE_DEBUG("table_new");
return MpTable_New();
}
static int table_init(tableobject *self, PyObject *args, PyObject *kwds)
{
PyObject *arg = NULL;
static char *kwlist[] = {"items", 0};
int result = 0;
TABLE_DEBUG("table_init");
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:mp_table",
kwlist, &arg))
result = -1;
else if (arg != NULL) {
if (PyObject_HasAttrString(arg, "keys"))
result = table_merge(self, arg, 1);
else
result = table_mergefromseq2(self, arg, 1);
}
return result;
}
static long table_nohash(PyObject *self)
{
TABLE_DEBUG("table_nohash");
PyErr_SetString(PyExc_TypeError, "mp_table objects are unhashable");
return -1;
}
static PyObject *table_iter(tableobject *self)
{
TABLE_DEBUG("table_iter");
return tableiter_new(self, select_key);
}
static char mp_table_doc[] =
"table() -> new empty table.\n"
"table(mapping) -> new table initialized from a mapping object's\n"
" (key, value) pairs.\n"
"table(seq) -> new table initialized as if via:\n"
" d = {}\n"
" for k, v in seq:\n"
" d[k] = v";
PyTypeObject MpTable_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"mp_table", /* tp_name */
sizeof(tableobject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)table_dealloc, /* tp_dealloc */
(printfunc)table_print, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
#if PY_MAJOR_VERSION < 3
(cmpfunc)table_compare, /* tp_compare */
#else
0, /* tp_reserved */
#endif
(reprfunc)table_repr, /* tp_repr */
0, /* tp_as_number */
&table_as_sequence, /* tp_as_sequence */
&table_as_mapping, /* tp_as_mapping */
table_nohash, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
mp_table_doc, /* tp_doc */
(traverseproc)table_traverse, /* tp_traverse */
/* PYTHON 2.5: 'inquiry' should be perhaps replaced with 'lenfunc' */
(inquiry)table_tp_clear, /* tp_clear */
table_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)table_iter, /* tp_iter */
0, /* tp_iternext */
mp_table_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)table_init, /* tp_init */
0, /* tp_alloc */
table_new, /* tp_new */
table_dealloc, /* tp_free */
};
/* Table iterator type */
extern PyTypeObject MpTableIter_Type; /* Forward */
typedef struct {
PyObject_HEAD
tableobject *table;
int ti_nelts;
int ti_pos;
tableselectfunc ti_select;
} tableiterobject;
static PyObject *tableiter_new(tableobject *table, tableselectfunc select)
{
tableiterobject *ti;
TABLE_DEBUG("tableiter_new");
ti = PyObject_NEW(tableiterobject, &MpTableIter_Type);
if (ti == NULL)
return NULL;
Py_INCREF(table);
ti->table = table;
ti->ti_nelts = table->table->a.nelts;
ti->ti_pos = 0;
ti->ti_select = select;
return (PyObject *)ti;
}
static void tableiter_dealloc(tableiterobject *ti)
{
Py_DECREF(ti->table);
PyObject_DEL(ti);
}
static PyObject *tableiter_next(tableiterobject *ti, PyObject *args)
{
apr_table_entry_t *elts = (apr_table_entry_t *) ti->table->table->a.elts;
TABLE_DEBUG("tableiter_next");
/* make sure the table hasn't change while being iterated */
if (ti->ti_nelts != ti->table->table->a.nelts) {
PyErr_SetString(PyExc_RuntimeError,
"table changed size during iteration");
return NULL;
}
/* return the next key/val */
if (ti->ti_pos < ti->table->table->a.nelts) {
return (*ti->ti_select)(&elts[ti->ti_pos++]);
}
/* the end has been reached */
PyErr_SetObject(PyExc_StopIteration, Py_None);
return NULL;
}
static PyObject *tableiter_getiter(PyObject *it)
{
TABLE_DEBUG("tableiter_getiter");
Py_INCREF(it);
return it;
}
static PyMethodDef tableiter_methods[] = {
{"next", (PyCFunction)tableiter_next, METH_VARARGS,
"it.next() -- get the next value, or raise StopIteration"},
{NULL, NULL} /* sentinel */
};
static PyObject *tableiter_iternext(tableiterobject *ti)
{
apr_table_entry_t *elts = (apr_table_entry_t *) ti->table->table->a.elts;
TABLE_DEBUG("tableiter_iternext");
/* make sure the table hasn't change while being iterated */
if (ti->ti_nelts != ti->table->table->a.nelts) {
PyErr_SetString(PyExc_RuntimeError,
"table changed size during iteration");
return NULL;
}
/* return the next key/val */
if (ti->ti_pos < ti->table->table->a.nelts) {
return (*ti->ti_select)(&elts[ti->ti_pos++]);
}
/* the end has been reached */
PyErr_SetObject(PyExc_StopIteration, Py_None);
return NULL;
}
PyTypeObject MpTableIter_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"dictionary-iterator", /* tp_name */
sizeof(tableiterobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)tableiter_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, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)tableiter_getiter, /* tp_iter */
(iternextfunc)tableiter_iternext, /* tp_iternext */
tableiter_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
};