chiark / gitweb /
pyke/mapping.c: Introduce macro for unconstifying common keyword list.
[mLib-python] / mapping.c
1 /* -*-c-*-
2  *
3  * Generic mapping support
4  *
5  * (c) 2019 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Pyke: the Python Kit for Extensions.
11  *
12  * Pyke is free software: you can redistribute it and/or modify it under
13  * the terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 2 of the License, or (at your
15  * option) any later version.
16  *
17  * Pyke is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20  * for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with Pyke.  If not, write to the Free Software Foundation, Inc.,
24  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 /*----- Header files ------------------------------------------------------*/
28
29 #include "pyke.h"
30
31 /*----- Iteration ---------------------------------------------------------*/
32
33 static PyTypeObject *itemiter_pytype, *valiter_pytype;
34
35 typedef struct iter_pyobj {
36   PyObject_HEAD
37   PyObject *map;
38   PyObject *i;
39 } iter_pyobj;
40 #define ITER_MAP(o) (((iter_pyobj *)(o))->map)
41 #define ITER_I(o) (((iter_pyobj *)(o))->i)
42
43 static void iter_pydealloc(PyObject *me)
44   { Py_DECREF(ITER_MAP(me)); Py_DECREF(ITER_I(me)); FREEOBJ(me); }
45
46 static PyObject *itemiter_pynext(PyObject *me)
47 {
48   PyObject *k = 0, *v = 0, *rc = 0;
49
50   if ((k = PyIter_Next(ITER_I(me))) != 0 &&
51       (v = PyObject_GetItem(ITER_MAP(me), k)) != 0)
52     rc = Py_BuildValue("(OO)", k, v);
53   Py_XDECREF(k); Py_XDECREF(v);
54   return (rc);
55 }
56
57 static const PyTypeObject itemiter_pytype_skel = {
58   PyObject_HEAD_INIT(0) 0,              /* Header */
59   "ItemIter",                           /* @tp_name@ */
60   sizeof(iter_pyobj),                   /* @tp_basicsize@ */
61   0,                                    /* @tp_itemsize@ */
62
63   iter_pydealloc,                       /* @tp_dealloc@ */
64   0,                                    /* @tp_print@ */
65   0,                                    /* @tp_getattr@ */
66   0,                                    /* @tp_setattr@ */
67   0,                                    /* @tp_compare@ */
68   0,                                    /* @tp_repr@ */
69   0,                                    /* @tp_as_number@ */
70   0,                                    /* @tp_as_sequence@ */
71   0,                                    /* @tp_as_mapping@ */
72   0,                                    /* @tp_hash@ */
73   0,                                    /* @tp_call@ */
74   0,                                    /* @tp_str@ */
75   0,                                    /* @tp_getattro@ */
76   0,                                    /* @tp_setattro@ */
77   0,                                    /* @tp_as_buffer@ */
78   Py_TPFLAGS_DEFAULT |                  /* @tp_flags@ */
79     Py_TPFLAGS_BASETYPE,
80
81   /* @tp_doc@ */
82   "Iterates over the items of a mapping.",
83
84   0,                                    /* @tp_traverse@ */
85   0,                                    /* @tp_clear@ */
86   0,                                    /* @tp_richcompare@ */
87   0,                                    /* @tp_weaklistoffset@ */
88   PyObject_SelfIter,                    /* @tp_iter@ */
89   itemiter_pynext,                      /* @tp_iternext@ */
90   0,                                    /* @tp_methods@ */
91   0,                                    /* @tp_members@ */
92   0,                                    /* @tp_getset@ */
93   0,                                    /* @tp_base@ */
94   0,                                    /* @tp_dict@ */
95   0,                                    /* @tp_descr_get@ */
96   0,                                    /* @tp_descr_set@ */
97   0,                                    /* @tp_dictoffset@ */
98   0,                                    /* @tp_init@ */
99   PyType_GenericAlloc,                  /* @tp_alloc@ */
100   abstract_pynew,                       /* @tp_new@ */
101   0,                                    /* @tp_free@ */
102   0                                     /* @tp_is_gc@ */
103 };
104
105 static PyObject *valiter_pynext(PyObject *me)
106 {
107   PyObject *k = 0, *rc = 0;
108
109   if ((k = PyIter_Next(ITER_I(me))) != 0)
110     rc = PyObject_GetItem(ITER_MAP(me), k);
111   Py_XDECREF(k);
112   return (rc);
113 }
114
115 static const PyTypeObject valiter_pytype_skel = {
116   PyObject_HEAD_INIT(0) 0,              /* Header */
117   "ValueIter",                          /* @tp_name@ */
118   sizeof(iter_pyobj),                   /* @tp_basicsize@ */
119   0,                                    /* @tp_itemsize@ */
120
121   iter_pydealloc,                       /* @tp_dealloc@ */
122   0,                                    /* @tp_print@ */
123   0,                                    /* @tp_getattr@ */
124   0,                                    /* @tp_setattr@ */
125   0,                                    /* @tp_compare@ */
126   0,                                    /* @tp_repr@ */
127   0,                                    /* @tp_as_number@ */
128   0,                                    /* @tp_as_sequence@ */
129   0,                                    /* @tp_as_mapping@ */
130   0,                                    /* @tp_hash@ */
131   0,                                    /* @tp_call@ */
132   0,                                    /* @tp_str@ */
133   0,                                    /* @tp_getattro@ */
134   0,                                    /* @tp_setattro@ */
135   0,                                    /* @tp_as_buffer@ */
136   Py_TPFLAGS_DEFAULT |                  /* @tp_flags@ */
137     Py_TPFLAGS_BASETYPE,
138
139   /* @tp_doc@ */
140   "Iterates over the values of a mapping.",
141
142   0,                                    /* @tp_traverse@ */
143   0,                                    /* @tp_clear@ */
144   0,                                    /* @tp_richcompare@ */
145   0,                                    /* @tp_weaklistoffset@ */
146   PyObject_SelfIter,                    /* @tp_iter@ */
147   valiter_pynext,                       /* @tp_iternext@ */
148   0,                                    /* @tp_methods@ */
149   0,                                    /* @tp_members@ */
150   0,                                    /* @tp_getset@ */
151   0,                                    /* @tp_base@ */
152   0,                                    /* @tp_dict@ */
153   0,                                    /* @tp_descr_get@ */
154   0,                                    /* @tp_descr_set@ */
155   0,                                    /* @tp_dictoffset@ */
156   0,                                    /* @tp_init@ */
157   PyType_GenericAlloc,                  /* @tp_alloc@ */
158   abstract_pynew,                       /* @tp_new@ */
159   0,                                    /* @tp_free@ */
160   0                                     /* @tp_is_gc@ */
161 };
162
163 const PySequenceMethods gmap_pysequence = {
164   0,                                    /* @sq_length@ */
165   0,                                    /* @sq_concat@ */
166   0,                                    /* @sq_repeat@ */
167   0,                                    /* @sq_item@ */
168   0,                                    /* @sq_slice@ */
169   0,                                    /* @sq_ass_item@ */
170   0,                                    /* @sq_ass_slice@ */
171   PyMapping_HasKey,                     /* @sq_contains@ */
172   0,                                    /* @sq_inplace_concat@ */
173   0                                     /* @sq_inplace_repeat@ */
174 };
175
176 /*----- Other mapping protocol support ------------------------------------*/
177
178 Py_ssize_t gmap_pysize(PyObject *me)
179 {
180   PyObject *i = 0, *x = 0;
181   Py_ssize_t rc = -1, n = 0;
182
183   if ((i = PyObject_GetIter(me)) == 0) goto done;
184   while ((x = PyIter_Next(i)) != 0) { n++; Py_DECREF(x); x = 0; }
185   if (PyErr_Occurred()) goto done;
186   rc = n;
187 done:
188   Py_XDECREF(i); Py_XDECREF(x);
189   return (rc);
190 }
191
192 PyObject *gmapmeth_has_key(PyObject *me, PyObject *arg)
193 {
194   PyObject *k;
195   if (!PyArg_ParseTuple(arg, "O:has_key", &k)) return (0);
196   return (getbool(PyMapping_HasKey(me, k)));
197 }
198
199 PyObject *gmapmeth_keys(PyObject *me)
200 {
201   PyObject *l = 0, *i = 0, *k, *rc = 0;
202   int err;
203
204   if ((l = PyList_New(0)) == 0 ||
205       (i = PyObject_GetIter(me)) == 0)
206     goto done;
207   while ((k = PyIter_Next(i)) != 0)
208     { err = PyList_Append(l, k); Py_DECREF(k); if (err) goto done; }
209   if (PyErr_Occurred()) goto done;
210   rc = l; l = 0;
211 done:
212   Py_XDECREF(l); Py_XDECREF(i);
213   return (rc);
214 }
215
216 PyObject *gmapmeth_values(PyObject *me)
217 {
218   PyObject *l = 0, *i = 0, *k, *v, *rc = 0;
219   int err = 0;
220
221   if ((l = PyList_New(0)) == 0 ||
222       (i = PyObject_GetIter(me)) == 0)
223     goto done;
224   while ((k = PyIter_Next(i)) != 0) {
225     if ((v = PyObject_GetItem(me, k)) == 0 ||
226         PyList_Append(l, v))
227       err = -1;
228     Py_DECREF(k); Py_XDECREF(v);
229     if (err) goto done;
230   }
231   if (PyErr_Occurred()) goto done;
232   rc = l; l = 0;
233 done:
234   Py_XDECREF(l); Py_XDECREF(i);
235   return (rc);
236 }
237
238 PyObject *gmapmeth_items(PyObject *me)
239 {
240   PyObject *l = 0, *i = 0, *k, *v, *z, *rc = 0;
241   int err = 0;
242
243   if ((l = PyList_New(0)) == 0 ||
244       (i = PyObject_GetIter(me)) == 0)
245     goto done;
246   while ((k = PyIter_Next(i)) != 0) {
247     z = 0;
248     if ((v = PyObject_GetItem(me, k)) == 0 ||
249         (z = Py_BuildValue("(OO)", k, v)) == 0 ||
250         PyList_Append(l, z))
251       err = -1;
252     Py_DECREF(k); Py_XDECREF(v); Py_XDECREF(z);
253     if (err) goto done;
254   }
255   if (PyErr_Occurred()) goto done;
256   rc = l; l = 0;
257 done:
258   Py_XDECREF(l); Py_XDECREF(i);
259   return (rc);
260 }
261
262 PyObject *gmapmeth_iterkeys(PyObject *me)
263   { return (PyObject_GetIter(me)); }
264
265 PyObject *gmapmeth_itervalues(PyObject *me)
266 {
267   PyObject *i;
268   iter_pyobj *ii;
269
270   if ((i = PyObject_GetIter(me)) == 0)
271     return (0);
272   ii = PyObject_NEW(iter_pyobj, valiter_pytype);
273   ii->map = me; Py_INCREF(me);
274   ii->i = i;
275   return ((PyObject *)ii);
276 }
277
278 PyObject *gmapmeth_iteritems(PyObject *me)
279 {
280   PyObject *i;
281   iter_pyobj *ii;
282
283   if ((i = PyObject_GetIter(me)) == 0)
284     return (0);
285   ii = PyObject_NEW(iter_pyobj, itemiter_pytype);
286   ii->map = me; Py_INCREF(me);
287   ii->i = i;
288   return ((PyObject *)ii);
289 }
290
291 PyObject *gmapmeth_clear(PyObject *me)
292 {
293   PyObject *i = 0, *k = 0, *rc = 0;
294
295   if ((i = PyObject_GetIter(me)) == 0)
296     goto end;
297   while ((k = PyIter_Next(i)) != 0) {
298     PyObject_DelItem(me, k);
299     Py_DECREF(k);
300   }
301   if (PyErr_Occurred()) goto end;
302   rc = me; Py_INCREF(me);
303 end:
304   Py_XDECREF(i);
305   return (rc);
306 }
307
308 static const char *const def_kwlist[] = { "key", "default", 0 };
309 #define DEF_KWLIST ((/*unconst*/ char **)def_kwlist)
310
311 PyObject *gmapmeth_get(PyObject *me, PyObject *arg, PyObject *kw)
312 {
313   PyObject *k, *def = Py_None, *v;
314
315   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get", DEF_KWLIST, &k, &def))
316     return (0);
317   if ((v = PyObject_GetItem(me, k)) != 0) return (v);
318   PyErr_Clear();
319   RETURN_OBJ(def);
320 }
321
322 PyObject *gmapmeth_setdefault(PyObject *me, PyObject *arg, PyObject *kw)
323 {
324   PyObject *k, *def = Py_None, *v;
325
326   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault", DEF_KWLIST,
327                                    &k, &def))
328     return (0);
329   if ((v = PyObject_GetItem(me, k)) != 0) return (v);
330   PyErr_Clear();
331   if (PyObject_SetItem(me, k, def)) return (0);
332   RETURN_OBJ(def);
333 }
334
335 PyObject *gmapmeth_pop(PyObject *me, PyObject *arg, PyObject *kw)
336 {
337   PyObject *k, *def = 0, *v;
338
339   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop", DEF_KWLIST, &k, &def))
340     return (0);
341   if ((v = PyObject_GetItem(me, k)) != 0) {
342     PyObject_DelItem(me, k);
343     return (v);
344   } else if (def) {
345     PyErr_Clear();
346     RETURN_OBJ(def);
347   } else
348     return (0);
349 }
350
351 PyObject *gmapmeth_update(PyObject *me, PyObject *arg)
352 {
353   PyObject *map, *i = 0, *k, *v, *rc = 0;
354   int err = 0;
355
356   if (!PyArg_ParseTuple(arg, "O:update", &map) ||
357       (i = PyObject_GetIter(map)) == 0)
358     goto end;
359   while ((k = PyIter_Next(i)) != 0) {
360     if ((v = PyObject_GetItem(map, k)) == 0 ||
361         PyObject_SetItem(me, k, v))
362       err = -1;
363     Py_DECREF(k); Py_XDECREF(v);
364     if (err) goto end;
365   }
366   if (PyErr_Occurred()) goto end;
367   rc = me; Py_INCREF(me);
368 end:
369   Py_XDECREF(i);
370   return (rc);
371 }
372
373 PyObject *gmapmeth_popitem(PyObject *me)
374 {
375   PyObject *i = 0, *k = 0, *v = 0, *rc = 0;
376
377   if ((i = PyObject_GetIter(me)) == 0)
378     goto end;
379   if ((k = PyIter_Next(i)) == 0) {
380     if (!PyErr_Occurred()) VALERR("popitem(): mapping is empty");
381     goto end;
382   }
383   if ((v = PyObject_GetItem(me, k)) == 0 ||
384       PyObject_DelItem(me, k))
385     goto end;
386   rc = Py_BuildValue("(OO)", k, v);
387 end:
388   Py_XDECREF(i); Py_XDECREF(k); Py_XDECREF(v);
389   return (rc);
390 }
391
392 const PyMethodDef gmap_pymethods[] = {
393   GMAP_METHODS
394   { 0 }
395 };
396
397 /*----- Submodule initialization ------------------------------------------*/
398
399 void pyke_gmap_pyinit(void)
400 {
401   INITTYPE(itemiter, root);
402   INITTYPE(valiter, root);
403 }
404
405 void pyke_gmap_pyinsert(PyObject *mod)
406 {
407   INSERT("ItemIter", itemiter_pytype);
408   INSERT("ValueIter", valiter_pytype);
409 }
410
411 /*----- That's all, folks -------------------------------------------------*/