Commit | Line | Data |
---|---|---|
361fd0e6 MW |
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 | ||
637b9140 | 163 | const PySequenceMethods gmap_pysequence = { |
361fd0e6 MW |
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 | ||
637b9140 | 406 | const PyMethodDef gmap_pymethods[] = { |
361fd0e6 MW |
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 -------------------------------------------------*/ |