chiark / gitweb /
pyke/pyke.h, key.c: Rename `INDEXERR' to `MAPERR'.
[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 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 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 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, PyObject *arg)
200 {
201   PyObject *l = 0, *i = 0, *k, *rc = 0;
202   int err;
203
204   if (!PyArg_ParseTuple(arg, ":keys") ||
205       (l = PyList_New(0)) == 0 ||
206       (i = PyObject_GetIter(me)) == 0)
207     goto done;
208   while ((k = PyIter_Next(i)) != 0)
209     { err = PyList_Append(l, k); Py_DECREF(k); if (err) goto done; }
210   if (PyErr_Occurred()) goto done;
211   rc = l; l = 0;
212 done:
213   Py_XDECREF(l); Py_XDECREF(i);
214   return (rc);
215 }
216
217 PyObject *gmapmeth_values(PyObject *me, PyObject *arg)
218 {
219   PyObject *l = 0, *i = 0, *k, *v, *rc = 0;
220   int err = 0;
221
222   if (!PyArg_ParseTuple(arg, ":values") ||
223       (l = PyList_New(0)) == 0 ||
224       (i = PyObject_GetIter(me)) == 0)
225     goto done;
226   while ((k = PyIter_Next(i)) != 0) {
227     if ((v = PyObject_GetItem(me, k)) == 0 ||
228         PyList_Append(l, v))
229       err = -1;
230     Py_DECREF(k); Py_XDECREF(v);
231     if (err) goto done;
232   }
233   if (PyErr_Occurred()) goto done;
234   rc = l; l = 0;
235 done:
236   Py_XDECREF(l); Py_XDECREF(i);
237   return (rc);
238 }
239
240 PyObject *gmapmeth_items(PyObject *me, PyObject *arg)
241 {
242   PyObject *l = 0, *i = 0, *k, *v, *z, *rc = 0;
243   int err = 0;
244
245   if (!PyArg_ParseTuple(arg, ":items") ||
246       (l = PyList_New(0)) == 0 ||
247       (i = PyObject_GetIter(me)) == 0)
248     goto done;
249   while ((k = PyIter_Next(i)) != 0) {
250     z = 0;
251     if ((v = PyObject_GetItem(me, k)) == 0 ||
252         (z = Py_BuildValue("(OO)", k, v)) == 0 ||
253         PyList_Append(l, z))
254       err = -1;
255     Py_DECREF(k); Py_XDECREF(v); Py_XDECREF(z);
256     if (err) goto done;
257   }
258   if (PyErr_Occurred()) goto done;
259   rc = l; l = 0;
260 done:
261   Py_XDECREF(l); Py_XDECREF(i);
262   return (rc);
263 }
264
265 PyObject *gmapmeth_iterkeys(PyObject *me, PyObject *arg)
266 {
267   if (!PyArg_ParseTuple(arg, ":iterkeys")) return (0);
268   return (PyObject_GetIter(me));
269 }
270
271 PyObject *gmapmeth_itervalues(PyObject *me, PyObject *arg)
272 {
273   PyObject *i;
274   iter_pyobj *ii;
275
276   if (!PyArg_ParseTuple(arg, ":itervalues") ||
277       (i = PyObject_GetIter(me)) == 0)
278     return (0);
279   ii = PyObject_NEW(iter_pyobj, valiter_pytype);
280   ii->map = me; Py_INCREF(me);
281   ii->i = i;
282   return ((PyObject *)ii);
283 }
284
285 PyObject *gmapmeth_iteritems(PyObject *me, PyObject *arg)
286 {
287   PyObject *i;
288   iter_pyobj *ii;
289
290   if (!PyArg_ParseTuple(arg, ":iteritems") ||
291       (i = PyObject_GetIter(me)) == 0)
292     return (0);
293   ii = PyObject_NEW(iter_pyobj, itemiter_pytype);
294   ii->map = me; Py_INCREF(me);
295   ii->i = i;
296   return ((PyObject *)ii);
297 }
298
299 PyObject *gmapmeth_clear(PyObject *me, PyObject *arg)
300 {
301   PyObject *i = 0, *k = 0, *rc = 0;
302
303   if (!PyArg_ParseTuple(arg, ":clear") ||
304       (i = PyObject_GetIter(me)) == 0)
305     goto end;
306   while ((k = PyIter_Next(i)) != 0) {
307     PyObject_DelItem(me, k);
308     Py_DECREF(k);
309   }
310   if (PyErr_Occurred()) goto end;
311   rc = me; Py_INCREF(me);
312 end:
313   Py_XDECREF(i);
314   return (rc);
315 }
316
317 static const char *const def_kwlist[] = { "key", "default", 0 };
318
319 PyObject *gmapmeth_get(PyObject *me, PyObject *arg, PyObject *kw)
320 {
321   PyObject *k, *def = Py_None, *v;
322
323   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get",
324                                    (/*unconst*/ char **)def_kwlist,
325                                    &k, &def))
326     return (0);
327   if ((v = PyObject_GetItem(me, k)) != 0) return (v);
328   PyErr_Clear();
329   RETURN_OBJ(def);
330 }
331
332 PyObject *gmapmeth_setdefault(PyObject *me, PyObject *arg, PyObject *kw)
333 {
334   PyObject *k, *def = Py_None, *v;
335
336   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault",
337                                    (/*unconst*/ char **)def_kwlist,
338                                    &k, &def))
339     return (0);
340   if ((v = PyObject_GetItem(me, k)) != 0) return (v);
341   PyErr_Clear();
342   if (PyObject_SetItem(me, k, def)) return (0);
343   RETURN_OBJ(def);
344 }
345
346 PyObject *gmapmeth_pop(PyObject *me, PyObject *arg, PyObject *kw)
347 {
348   PyObject *k, *def = 0, *v;
349
350   if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop",
351                                    (/*unconst*/ char **)def_kwlist,
352                                    &k, &def))
353     return (0);
354   if ((v = PyObject_GetItem(me, k)) != 0) {
355     PyObject_DelItem(me, k);
356     return (v);
357   } else if (def) {
358     PyErr_Clear();
359     RETURN_OBJ(def);
360   } else
361     return (0);
362 }
363
364 PyObject *gmapmeth_update(PyObject *me, PyObject *arg)
365 {
366   PyObject *map, *i = 0, *k, *v, *rc = 0;
367   int err = 0;
368
369   if (!PyArg_ParseTuple(arg, "O:update", &map) ||
370       (i = PyObject_GetIter(map)) == 0)
371     goto end;
372   while ((k = PyIter_Next(i)) != 0) {
373     if ((v = PyObject_GetItem(map, k)) == 0 ||
374         PyObject_SetItem(me, k, v))
375       err = -1;
376     Py_DECREF(k); Py_XDECREF(v);
377     if (err) goto end;
378   }
379   if (PyErr_Occurred()) goto end;
380   rc = me; Py_INCREF(me);
381 end:
382   Py_XDECREF(i);
383   return (rc);
384 }
385
386 PyObject *gmapmeth_popitem(PyObject *me, PyObject *arg)
387 {
388   PyObject *i = 0, *k = 0, *v = 0, *rc = 0;
389
390   if (!PyArg_ParseTuple(arg, ":popitem") ||
391       (i = PyObject_GetIter(me)) == 0)
392     goto end;
393   if ((k = PyIter_Next(i)) == 0) {
394     if (!PyErr_Occurred()) VALERR("popitem(): mapping is empty");
395     goto end;
396   }
397   if ((v = PyObject_GetItem(me, k)) == 0 ||
398       PyObject_DelItem(me, k))
399     goto end;
400   rc = Py_BuildValue("(OO)", k, v);
401 end:
402   Py_XDECREF(i); Py_XDECREF(k); Py_XDECREF(v);
403   return (rc);
404 }
405
406 PyMethodDef gmap_pymethods[] = {
407   GMAP_METHODS
408   { 0 }
409 };
410
411 /*----- Submodule initialization ------------------------------------------*/
412
413 void pyke_gmap_pyinit(void)
414 {
415   INITTYPE(itemiter, root);
416   INITTYPE(valiter, root);
417 }
418
419 void pyke_gmap_pyinsert(PyObject *mod)
420 {
421   INSERT("ItemIter", itemiter_pytype);
422   INSERT("ValueIter", valiter_pytype);
423 }
424
425 /*----- That's all, folks -------------------------------------------------*/