3 * Generic mapping support
5 * (c) 2019 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Pyke: the Python Kit for Extensions.
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.
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
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.
27 /*----- Header files ------------------------------------------------------*/
31 /*----- Iteration ---------------------------------------------------------*/
33 static PyTypeObject *keyiter_pytype, *itemiter_pytype, *valiter_pytype;
40 typedef struct iter_pyobj {
45 #define ITER_MAP(o) (((iter_pyobj *)(o))->map)
46 #define ITER_ITER(o) (&((iter_pyobj *)(o))->iter)
47 #define ITER_EXTERNALP(o) \
48 (GMAP_OPS(ITER_MAP(o))->isz > sizeof(union iterstate))
49 #define ITER_I(o) (ITER_EXTERNALP(o) ? ITER_ITER(o)->external \
50 : &ITER_ITER(o)->internal)
52 static void *iter_init(PyObject *me, union iterstate *iter)
54 const gmap_ops *gmops = GMAP_OPS(me);
57 if (gmops->isz <= sizeof(*iter)) i = &iter->internal;
58 else { i = iter->external = PyObject_Malloc(gmops->isz); assert(i); }
59 gmops->iter_init(me, i);
63 static void iter_free(PyObject *me, union iterstate *iter)
64 { if (GMAP_OPS(me)->isz > sizeof(*iter)) PyObject_Free(iter->external); }
66 static void iter_pydealloc(PyObject *me)
68 PyObject *map = ITER_MAP(me);
69 iter_free(map, ITER_ITER(me));
70 Py_DECREF(map); FREEOBJ(me);
73 static PyObject *gmap_mkiter(PyObject *me, PyTypeObject *ty)
75 iter_pyobj *iter = PyObject_NEW(iter_pyobj, ty);
77 iter->map = me; Py_INCREF(me);
78 iter_init(me, &iter->iter);
79 return ((PyObject *)iter);
82 static PyObject *keyiter_pynext(PyObject *me)
84 PyObject *map = ITER_MAP(me);
85 const struct gmap_ops *gmops = GMAP_OPS(map);
86 void *e = gmops->iter_next(map, ITER_I(me));
89 else return (gmops->entry_key(map, e));
92 static const PyTypeObject keyiter_pytype_skel = {
93 PyVarObject_HEAD_INIT(0, 0) /* Header */
94 "_KeyIter", /* @tp_name@ */
95 sizeof(iter_pyobj), /* @tp_basicsize@ */
96 0, /* @tp_itemsize@ */
98 iter_pydealloc, /* @tp_dealloc@ */
100 0, /* @tp_getattr@ */
101 0, /* @tp_setattr@ */
102 0, /* @tp_compare@ */
104 0, /* @tp_as_number@ */
105 0, /* @tp_as_sequence@ */
106 0, /* @tp_as_mapping@ */
110 0, /* @tp_getattro@ */
111 0, /* @tp_setattro@ */
112 0, /* @tp_as_buffer@ */
113 Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
117 "Iterates over the keys of a mapping.",
119 0, /* @tp_traverse@ */
121 0, /* @tp_richcompare@ */
122 0, /* @tp_weaklistoffset@ */
123 PyObject_SelfIter, /* @tp_iter@ */
124 keyiter_pynext, /* @tp_iternext@ */
125 0, /* @tp_methods@ */
126 0, /* @tp_members@ */
130 0, /* @tp_descr_get@ */
131 0, /* @tp_descr_set@ */
132 0, /* @tp_dictoffset@ */
134 PyType_GenericAlloc, /* @tp_alloc@ */
135 abstract_pynew, /* @tp_new@ */
140 static PyObject *valiter_pynext(PyObject *me)
142 PyObject *map = ITER_MAP(me);
143 const struct gmap_ops *gmops = GMAP_OPS(map);
144 void *e = gmops->iter_next(map, ITER_I(me));
147 else return (gmops->entry_value(map, e));
150 static const PyTypeObject valiter_pytype_skel = {
151 PyVarObject_HEAD_INIT(0, 0) /* Header */
152 "_ValueIter", /* @tp_name@ */
153 sizeof(iter_pyobj), /* @tp_basicsize@ */
154 0, /* @tp_itemsize@ */
156 iter_pydealloc, /* @tp_dealloc@ */
158 0, /* @tp_getattr@ */
159 0, /* @tp_setattr@ */
160 0, /* @tp_compare@ */
162 0, /* @tp_as_number@ */
163 0, /* @tp_as_sequence@ */
164 0, /* @tp_as_mapping@ */
168 0, /* @tp_getattro@ */
169 0, /* @tp_setattro@ */
170 0, /* @tp_as_buffer@ */
171 Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
175 "Iterates over the values of a mapping.",
177 0, /* @tp_traverse@ */
179 0, /* @tp_richcompare@ */
180 0, /* @tp_weaklistoffset@ */
181 PyObject_SelfIter, /* @tp_iter@ */
182 valiter_pynext, /* @tp_iternext@ */
183 0, /* @tp_methods@ */
184 0, /* @tp_members@ */
188 0, /* @tp_descr_get@ */
189 0, /* @tp_descr_set@ */
190 0, /* @tp_dictoffset@ */
192 PyType_GenericAlloc, /* @tp_alloc@ */
193 abstract_pynew, /* @tp_new@ */
198 static PyObject *itemiter_pynext(PyObject *me)
200 PyObject *map = ITER_MAP(me);
201 const struct gmap_ops *gmops = GMAP_OPS(map);
202 void *e = gmops->iter_next(map, ITER_I(me));
206 rc = Py_BuildValue("(NN)",
207 gmops->entry_key(map, e),
208 gmops->entry_value(map, e));
212 static const PyTypeObject itemiter_pytype_skel = {
213 PyVarObject_HEAD_INIT(0, 0) /* Header */
214 "_ItemIter", /* @tp_name@ */
215 sizeof(iter_pyobj), /* @tp_basicsize@ */
216 0, /* @tp_itemsize@ */
218 iter_pydealloc, /* @tp_dealloc@ */
220 0, /* @tp_getattr@ */
221 0, /* @tp_setattr@ */
222 0, /* @tp_compare@ */
224 0, /* @tp_as_number@ */
225 0, /* @tp_as_sequence@ */
226 0, /* @tp_as_mapping@ */
230 0, /* @tp_getattro@ */
231 0, /* @tp_setattro@ */
232 0, /* @tp_as_buffer@ */
233 Py_TPFLAGS_DEFAULT | /* @tp_flags@ */
237 "Iterates over the items of a mapping.",
239 0, /* @tp_traverse@ */
241 0, /* @tp_richcompare@ */
242 0, /* @tp_weaklistoffset@ */
243 PyObject_SelfIter, /* @tp_iter@ */
244 itemiter_pynext, /* @tp_iternext@ */
245 0, /* @tp_methods@ */
246 0, /* @tp_members@ */
250 0, /* @tp_descr_get@ */
251 0, /* @tp_descr_set@ */
252 0, /* @tp_dictoffset@ */
254 PyType_GenericAlloc, /* @tp_alloc@ */
255 abstract_pynew, /* @tp_new@ */
260 /*----- Other mapping protocol support ------------------------------------*/
262 Py_ssize_t gmap_pysize(PyObject *me)
264 const gmap_ops *gmops = GMAP_OPS(me);
265 union iterstate iter;
269 i = iter_init(me, &iter);
270 while (gmops->iter_next(me, i)) n++;
271 iter_free(me, &iter);
275 PyObject *gmap_pylookup(PyObject *me, PyObject *key)
277 const gmap_ops *gmops = GMAP_OPS(me);
278 void *e = gmops->lookup(me, key, 0);
281 if (!e) { if (!PyErr_Occurred()) MAPERR(key); else goto end; }
282 rc = gmops->entry_value(me, e);
287 int gmap_pystore(PyObject *me, PyObject *key, PyObject *value)
289 const gmap_ops *gmops = GMAP_OPS(me);
291 void *e = gmops->lookup(me, key, &f);
296 rc = gmops->del_entry(me, e);
298 rc = gmops->set_entry(me, e, value);
299 if (rc && !f) gmops->del_entry(me, e);
306 int gmap_pyhaskey(PyObject *me, PyObject *key)
307 { return (GMAP_OPS(me)->lookup(me, key, 0) ? 1 : PyErr_Occurred() ? -1 : 0); }
309 const PySequenceMethods gmap_pysequence = {
315 0, /* @sq_ass_item@ */
316 0, /* @sq_ass_slice@ */
317 gmap_pyhaskey, /* @sq_contains@ */
318 0, /* @sq_inplace_concat@ */
319 0 /* @sq_inplace_repeat@ */
322 PyObject *gmapmeth_has_key(PyObject *me, PyObject *arg)
326 if (!PyArg_ParseTuple(arg, "O:has_key", &k)) return (0);
327 e = GMAP_OPS(me)->lookup(me, k, 0);
329 else if (!PyErr_Occurred()) RETURN_FALSE;
333 PyObject *gmapmeth_keys(PyObject *me)
335 const gmap_ops *gmops = GMAP_OPS(me);
336 union iterstate iter; void *i = 0, *e;
337 PyObject *l = 0, *k, *rc = 0;
340 if ((l = PyList_New(0)) == 0) goto done;
341 i = iter_init(me, &iter);
342 while ((e = gmops->iter_next(me, i)) != 0) {
343 k = gmops->entry_key(me, e);
344 err = PyList_Append(l, k);
351 if (i) iter_free(me, &iter);
355 PyObject *gmapmeth_values(PyObject *me)
357 const gmap_ops *gmops = GMAP_OPS(me);
358 union iterstate iter; void *i = 0, *e;
359 PyObject *l = 0, *v, *rc = 0;
362 if ((l = PyList_New(0)) == 0) goto done;
363 i = iter_init(me, &iter);
364 while ((e = gmops->iter_next(me, i)) != 0) {
365 v = gmops->entry_value(me, e);
366 err = PyList_Append(l, v);
373 if (i) iter_free(me, &iter);
377 PyObject *gmapmeth_items(PyObject *me)
379 const gmap_ops *gmops = GMAP_OPS(me);
380 union iterstate iter; void *i = 0, *e;
381 PyObject *l = 0, *z, *rc = 0;
384 if ((l = PyList_New(0)) == 0) goto done;
385 i = iter_init(me, &iter);
386 while ((e = gmops->iter_next(me, i)) != 0) {
387 if ((z = Py_BuildValue("(NN)",
388 gmops->entry_key(me, e),
389 gmops->entry_value(me, e))) == 0)
391 err = PyList_Append(l, z);
398 if (i) iter_free(me, &iter);
402 PyObject *gmapmeth_iterkeys(PyObject *me)
403 { return (gmap_mkiter(me, keyiter_pytype)); }
405 PyObject *gmapmeth_itervalues(PyObject *me)
406 { return (gmap_mkiter(me, valiter_pytype)); }
408 PyObject *gmapmeth_iteritems(PyObject *me)
409 { return (gmap_mkiter(me, itemiter_pytype)); }
411 PyObject *gmap_pyiter(PyObject *me)
412 { return gmap_mkiter(me, keyiter_pytype); }
414 PyObject *gmapmeth_clear(PyObject *me)
416 const gmap_ops *gmops = GMAP_OPS(me);
417 union iterstate iter;
421 i = iter_init(me, &iter);
423 e = gmops->iter_next(me, i); if (!e) break;
424 if (gmops->del_entry(me, e)) goto end;
426 iter_free(me, &iter);
427 rc = me; Py_INCREF(me);
432 static const char *const def_kwlist[] = { "key", "default", 0 };
433 #define DEF_KWLIST ((/*unconst*/ char **)def_kwlist)
435 PyObject *gmapmeth_get(PyObject *me, PyObject *arg, PyObject *kw)
437 const gmap_ops *gmops = GMAP_OPS(me);
438 PyObject *k, *def = Py_None;
441 if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:get", DEF_KWLIST, &k, &def))
443 e = gmops->lookup(me, k, 0);
444 if (e) return (gmops->entry_value(me, e));
445 else if (!PyErr_Occurred()) RETURN_OBJ(def);
449 PyObject *gmapmeth_setdefault(PyObject *me, PyObject *arg, PyObject *kw)
451 const gmap_ops *gmops = GMAP_OPS(me);
452 PyObject *k, *def = Py_None;
456 if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:setdefault", DEF_KWLIST,
459 e = gmops->lookup(me, k, &f);
461 else if (f) return (gmops->entry_value(me, e));
462 else if (gmops->set_entry(me, e, def)) return (0);
463 else RETURN_OBJ(def);
466 PyObject *gmapmeth_pop(PyObject *me, PyObject *arg, PyObject *kw)
468 const gmap_ops *gmops = GMAP_OPS(me);
469 PyObject *k, *def = 0;
473 if (!PyArg_ParseTupleAndKeywords(arg, kw, "O|O:pop", DEF_KWLIST, &k, &def))
475 e = gmops->lookup(me, k, 0);
477 if (PyErr_Occurred()) goto end;
478 else if (def) { rc = def; Py_INCREF(rc); }
481 rc = gmops->entry_value(me, e);
482 if (gmops->del_entry(me, e)) { Py_DECREF(rc); rc = 0; }
488 static int update_core(PyObject *me, PyObject *map)
490 const gmap_ops *gmops = GMAP_OPS(me);
491 PyObject *i = 0, *item = 0, *k = 0, *v = 0;
496 v = PyObject_CallMethod(map, "iteritems", 0);
501 item = PyIter_Next(i); if (!item) break;
502 if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2)
503 TYERR("wanted a pair");
504 k = PyTuple_GET_ITEM(item, 0); Py_INCREF(k);
505 v = PyTuple_GET_ITEM(item, 1); Py_INCREF(v);
506 e = gmops->lookup(me, k, &foundp); if (!e) goto end;
507 if (gmops->set_entry(me, e, v)) goto end;
508 Py_DECREF(item); Py_DECREF(k); Py_DECREF(v); item = k = v = 0;
510 if (PyErr_Occurred()) goto end;
513 i = PyObject_GetIter(map); if (!i) goto end;
515 k = PyIter_Next(i); if (!k) goto end;
516 v = PyObject_GetItem(map, k); if (!v) goto end;
517 e = gmops->lookup(me, k, &foundp); if (!e) goto end;
518 if (gmops->set_entry(me, e, v)) goto end;
519 Py_DECREF(k); Py_DECREF(v); k = v = 0;
521 if (PyErr_Occurred()) goto end;
525 Py_XDECREF(i); Py_XDECREF(item);
526 Py_XDECREF(k); Py_XDECREF(v);
530 PyObject *gmapmeth_update(PyObject *me, PyObject *arg, PyObject *kw)
534 if (!PyArg_ParseTuple(arg, "|O:update", &map)) return (0);
535 if (map && update_core(me, map)) return (0);
536 if (kw && update_core(me, kw)) return (0);
540 PyObject *gmapmeth_popitem(PyObject *me)
542 const gmap_ops *gmops = GMAP_OPS(me);
543 union iterstate iter;
548 i = iter_init(me, &iter);
549 e = gmops->iter_next(me, i);
550 iter_free(me, &iter);
554 rc = Py_BuildValue("(NN)",
555 gmops->entry_key(me, e), gmops->entry_value(me, e));
556 if (gmops->del_entry(me, e)) { Py_DECREF(rc); rc = 0; }
562 const PyMethodDef gmapro_pymethods[] = {
567 const PyMethodDef gmap_pymethods[] = {
572 /*----- Submodule initialization ------------------------------------------*/
574 void pyke_gmap_pyinit(void)
576 INITTYPE(keyiter, root);
577 INITTYPE(itemiter, root);
578 INITTYPE(valiter, root);
581 void pyke_gmap_pyinsert(PyObject *mod)
583 INSERT("_KeyIter", keyiter_pytype);
584 INSERT("_ValueIter", valiter_pytype);
585 INSERT("_ItemIter", itemiter_pytype);
588 /*----- That's all, folks -------------------------------------------------*/